#![deny(missing_docs)]
use std::fmt::{Display, Formatter};
pub struct Code<'a> {
code: String,
requires: Vec<&'a str>,
exit: i32,
}
#[derive(Debug, Clone, Copy)]
pub enum CArg<'a> {
String(&'a str),
Ident(&'a str),
Int32(i32),
Int64(i64),
Float(f32),
Double(f64),
Bool(bool),
Char(char),
}
#[derive(Debug, Clone, Copy)]
pub enum VarTypes {
String,
Int32,
Int64,
Float,
Double,
Bool,
Char,
}
#[derive(Debug, Clone, Copy)]
pub enum VarInit<'a> {
String(&'a str),
Ident(VarTypes, &'a str),
Int32(i32),
Int64(i64),
Float(f32),
Double(f64),
Bool(bool),
Char(char),
SizeString(usize),
}
impl Default for Code<'_> {
fn default() -> Self {
Self::new()
}
}
impl Code<'_> {
pub fn new() -> Self {
Self {
code: String::new(),
requires: vec![],
exit: 0,
}
}
pub fn exit(&mut self, code: i32) {
self.exit = code;
}
pub fn include(&mut self, file: &'static str) {
if self.requires.contains(&file) {
return;
}
self.requires.push(file);
}
pub fn call_func(&mut self, func: &str) {
self.code.push_str(func);
self.code.push_str("();\n")
}
pub fn call_func_with_args(&mut self, func: &str, args: Vec<CArg>) {
self.code.push_str(func);
self.code.push('(');
for arg in args {
match arg {
CArg::String(s) => {
let s = s.replace("\r\n", "\\r\\n");
let s = s.replace('\n', "\\n");
let s = s.replace('\t', "\\t");
let s = s.replace('"', "\\\"");
self.code.push('"');
self.code.push_str(s.as_str());
self.code.push('"');
}
CArg::Ident(id) => {
self.code.push_str(id);
}
CArg::Int32(n) => {
self.code.push_str(&n.to_string());
}
CArg::Int64(n) => {
self.code.push_str(&n.to_string());
}
CArg::Float(n) => {
self.code.push_str(&n.to_string());
}
CArg::Double(n) => {
self.code.push_str(&n.to_string());
}
CArg::Bool(b) => {
self.code.push_str(&b.to_string());
}
CArg::Char(c) => {
self.code.push(c);
}
}
self.code.push(',');
}
if self.code.ends_with(',') {
self.code = self.code.strip_suffix(',').unwrap().to_string();
}
self.code.push_str(");\n")
}
pub fn new_var<S: AsRef<str>>(&mut self, name: S, value: VarInit) {
let name = name.as_ref();
match value {
VarInit::String(s) => {
self.code.push_str("char ");
self.code.push_str(name);
self.code.push_str("[]=\"");
self.code.push_str(s);
self.code.push_str("\";");
self.code.push('\n');
}
VarInit::Ident(ty, ident) => {
self.code.push_str(match ty {
VarTypes::String => "char ",
VarTypes::Int32 => "int ",
VarTypes::Int64 => "int ",
VarTypes::Float => "float ",
VarTypes::Double => "double ",
VarTypes::Bool => {
self.requires.push("stdbool.h");
"bool "
}
VarTypes::Char => "char ",
});
self.code.push_str(name);
if let VarTypes::String = ty {
self.code.push_str("[]");
}
self.code.push('=');
self.code.push_str(ident);
self.code.push(';');
self.code.push('\n');
}
VarInit::Bool(b) => {
self.requires.push("stdbool.h");
self.code.push_str("bool ");
self.code.push_str(name);
self.code.push('=');
self.code.push_str(&b.to_string());
self.code.push_str(";\n");
}
VarInit::Char(c) => {
self.code.push_str("char ");
self.code.push_str(name);
self.code.push_str("='");
self.code.push(c);
self.code.push_str("';\n");
}
VarInit::Double(f) => {
self.code.push_str("double ");
self.code.push_str(name);
self.code.push('=');
self.code.push_str(&f.to_string());
self.code.push_str(";\n");
}
VarInit::Float(f) => {
self.code.push_str("float ");
self.code.push_str(name);
self.code.push('=');
self.code.push_str(&f.to_string());
self.code.push_str(";\n");
}
VarInit::Int32(i) => {
self.code.push_str("int ");
self.code.push_str(name);
self.code.push('=');
self.code.push_str(&i.to_string());
self.code.push_str(";\n");
}
VarInit::Int64(i) => {
self.code.push_str("int ");
self.code.push_str(name);
self.code.push('=');
self.code.push_str(&i.to_string());
self.code.push_str(";\n");
}
VarInit::SizeString(size) => {
self.code.push_str("char ");
self.code.push_str(name);
self.code.push('[');
self.code.push_str(&size.to_string());
self.code.push_str("];\n");
}
}
}
}
impl Display for Code<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut require_string = String::new();
for require in &self.requires {
require_string.push_str("#include<");
require_string.push_str(require);
require_string.push_str(">\n");
}
writeln!(
f,
"{}int main() {{\n{}return {};\n}}",
require_string, self.code, self.exit
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty() {
let code = Code::new();
assert_eq!(code.to_string(), "int main() {\nreturn 0;\n}\n");
}
#[test]
fn test_exit_zero() {
let mut code = Code::new();
code.exit(0);
assert_eq!(code.to_string(), "int main() {\nreturn 0;\n}\n");
}
#[test]
fn test_exit_non_zero() {
let mut code = Code::new();
code.exit(1);
assert_eq!(code.to_string(), "int main() {\nreturn 1;\n}\n");
}
#[test]
fn test_multiple_exits() {
let mut code = Code::new();
code.exit(0);
code.exit(1);
assert_eq!(code.to_string(), "int main() {\nreturn 1;\n}\n");
}
#[test]
fn test_include_valid() {
let mut code = Code::new();
code.include("stdio.h");
assert!(code.to_string().contains("#include<stdio.h>"));
}
#[test]
fn test_func_no_args() {
let mut code = Code::new();
code.call_func("printf");
assert!(code.to_string().contains("printf();"));
}
#[test]
fn test_func_with_args() {
let mut code = Code::new();
code.call_func_with_args("printf", vec![CArg::String("Hello")]);
assert!(code.to_string().contains("printf(\"Hello\");"));
}
#[test]
fn test_variable_string() {
let mut code = Code::new();
code.new_var("msg", VarInit::String("Hello"));
assert!(code.to_string().contains("char msg[]=\"Hello\";"));
}
#[test]
fn test_variable_i32() {
let mut code = Code::new();
code.new_var("num", VarInit::Int32(i32::MAX));
assert!(code
.to_string()
.contains(format!("int num={};", i32::MAX).as_str()));
}
#[test]
fn test_variable_i64() {
let mut code = Code::new();
code.new_var("num", VarInit::Int64(i64::MAX));
assert!(code
.to_string()
.contains(format!("int num={};", i64::MAX).as_str()));
}
#[test]
fn test_variable_float() {
let mut code = Code::new();
code.new_var("num", VarInit::Float(f32::MAX));
assert!(code
.to_string()
.contains(format!("float num={};", f32::MAX).as_str()));
}
#[test]
fn test_variable_double() {
let mut code = Code::new();
code.new_var("num", VarInit::Double(f64::MAX));
assert!(code
.to_string()
.contains(format!("double num={};", f64::MAX).as_str()));
}
#[test]
fn test_variable_bool() {
let mut code = Code::new();
code.new_var("b", VarInit::Bool(true));
assert!(code.to_string().contains("bool b=true;"));
}
#[test]
fn test_variable_char() {
let mut code = Code::new();
code.new_var("c", VarInit::Char('c'));
assert!(code.to_string().contains("char c='c';"));
}
#[test]
fn test_variable_size_string() {
let mut code = Code::new();
code.new_var("msg", VarInit::SizeString(5));
assert!(code.to_string().contains("char msg[5];"));
}
#[test]
fn test_variable_ident() {
let mut code = Code::new();
code.new_var("s", VarInit::String("X"));
code.new_var("t", VarInit::Ident(VarTypes::String, "s"));
assert!(code.to_string().contains("char s[]=\"X\";\nchar t[]=s;"));
}
}