structdump 0.1.2

Dump structure to source code
Documentation
use std::fmt::Write;
use std::{collections::HashMap, path::PathBuf, rc::Rc};

pub trait Codegen {
	fn gen_code(&self, res: &mut CodegenResult, out: &mut String);
}
impl<'a, T> Codegen for &'a T
where
	T: Codegen,
{
	fn gen_code(&self, res: &mut CodegenResult, out: &mut String) {
		(*self).gen_code(res, out)
	}
}

#[derive(Default)]
pub struct CodegenResult {
	codes: HashMap<String, usize>,
	id: usize,
	snippets: Vec<String>,
}
impl CodegenResult {
	pub fn add_code(&mut self, code: String, ty: &str) -> String {
		let var_name = if !self.codes.contains_key(&code) {
			let value = self.id;
			self.id += 1;
			self.codes.insert(code.clone(), value);
			let var_name = format!("code_{:x}", value);
			self.snippets
				.push(format!("let {}{} = {};", var_name, ty, code));
			var_name
		} else {
			let value = self.codes.get(&code).unwrap();
			format!("code_{:x}", value)
		};
		format!("{}.clone()", var_name)
	}
	pub fn add_rc_value<T: Codegen + ?Sized>(&mut self, s: &T) -> String {
		let mut out = String::new();
		s.gen_code(self, &mut out);
		self.add_code(format!("::std::rc::Rc::new({})", out), "")
	}
	pub fn add_into_rc_value(&mut self, s: &impl Codegen, ty: &'static str) -> String {
		let mut out = String::new();
		s.gen_code(self, &mut out);
		self.add_code(
			format!("{}.into()", out),
			&format!(": ::std::rc::Rc<{}>", ty),
		)
	}
	pub fn add_value(&mut self, s: &impl Codegen) -> String {
		let mut out = String::new();
		s.gen_code(self, &mut out);
		out
	}
	pub fn codegen(&mut self, s: &impl Codegen) -> String {
		let mut out = String::new();
		s.gen_code(self, &mut out);
		let mut real_out = String::new();
		real_out.push_str("{");
		for snip in self.snippets.iter() {
			real_out.push_str(&snip);
			real_out.push_str("\n");
		}
		real_out.push_str(&out);
		real_out.push_str("}");
		real_out
	}
}

impl<T: Codegen> Codegen for &Option<T> {
	fn gen_code(&self, res: &mut CodegenResult, out: &mut String) {
		write!(out, "std::option::Option::").unwrap();
		if self.is_some() {
			let v = self.as_ref().unwrap();
			write!(out, "Some({})", res.add_value(v)).unwrap();
		} else {
			write!(out, "None").unwrap();
		}
	}
}
impl Codegen for Rc<str> {
	fn gen_code(&self, res: &mut CodegenResult, out: &mut String) {
		let s: &str = &*self;
		write!(out, "{}", res.add_into_rc_value(&s, "str")).unwrap()
	}
}
impl<T: Codegen> Codegen for Rc<T> {
	fn gen_code(&self, res: &mut CodegenResult, out: &mut String) {
		let v: &T = &**self;
		out.push_str(&res.add_rc_value(v));
	}
}
impl<T: Codegen> Codegen for Vec<T> {
	fn gen_code(&self, res: &mut CodegenResult, out: &mut String) {
		out.push_str("vec![");
		for i in self.iter() {
			out.push_str(&res.add_value(i));
			out.push(',');
		}
		out.push_str("]");
	}
}
impl Codegen for &str {
	fn gen_code(&self, _res: &mut CodegenResult, out: &mut String) {
		out.push('"');
		out.push_str(&self.escape_debug().collect::<String>());
		out.push('"');
	}
}
impl Codegen for String {
	fn gen_code(&self, _res: &mut CodegenResult, out: &mut String) {
		out.push('"');
		out.push_str(&self.escape_debug().collect::<String>());
		out.push_str("\".to_string()");
	}
}
impl Codegen for PathBuf {
	fn gen_code(&self, _res: &mut CodegenResult, out: &mut String) {
		out.push_str("std::path::PathBuf::from(\"");
		out.push_str(&self.to_str().unwrap().escape_debug().collect::<String>());
		out.push_str("\")");
	}
}
impl Codegen for &bool {
	fn gen_code(&self, _res: &mut CodegenResult, out: &mut String) {
		write!(out, "{}", self).unwrap()
	}
}

macro_rules! num_impl {
	($t:ty) => {
		impl Codegen for &$t {
			fn gen_code(&self, _res: &mut CodegenResult, out: &mut String) {
				write!(out, "{}{}", self, stringify!($t)).unwrap();
			}
		}
	};
}
num_impl!(u8);
num_impl!(u16);
num_impl!(u32);
num_impl!(u64);
num_impl!(i8);
num_impl!(i16);
num_impl!(i32);
num_impl!(i64);
num_impl!(f32);
num_impl!(f64);
num_impl!(isize);
num_impl!(usize);