#[cfg(test)]
use lisp_rpc_rust_parser::Parser;
#[cfg(test)]
use std::io::Cursor;
use super::*;
use anyhow::Context;
use lisp_rpc_rust_parser::{Atom, Expr, TypeValue};
use tera::Tera;
#[derive(Debug)]
enum DefPkgErrorType {
InvalidInput,
}
#[derive(Debug)]
struct DefPkgError {
msg: String,
err_type: DefPkgErrorType,
}
impl std::fmt::Display for DefPkgError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}: {}", self.err_type, self.msg)
}
}
impl Error for DefPkgError {}
#[doc = r#"the struct of def-rpc-package expression
(def-rpc-package demo)
"#]
#[derive(Debug, Eq, PartialEq)]
pub struct DefPkg {
pkg_name: String,
}
impl DefPkg {
pub fn if_def_pkg_expr(expr: &Expr) -> bool {
match &expr {
Expr::List(e) => match &e[0] {
Expr::Atom(Atom {
value: TypeValue::Symbol(s),
..
}) => s == "def-rpc-package",
_ => false,
},
_ => false,
}
}
pub fn from_expr(expr: &Expr) -> Result<Self> {
let rest_expr: &[Expr];
if Self::if_def_pkg_expr(expr) {
match &expr {
Expr::List(e) => rest_expr = &e[1..],
_ => {
anyhow::bail!(DefPkgError {
msg: "parsing failed, the first symbol should be def-rpc-package"
.to_string(),
err_type: DefPkgErrorType::InvalidInput,
});
}
}
} else {
anyhow::bail!(DefPkgError {
msg: "parsing failed, the first symbol should be def-rpc-package".to_string(),
err_type: DefPkgErrorType::InvalidInput,
});
}
let name = match &rest_expr[0] {
Expr::Atom(Atom {
value: TypeValue::Symbol(s),
..
}) => s,
_ => {
anyhow::bail!(DefPkgError {
msg: "parsing failed, pkg name should be symbol".to_string(),
err_type: DefPkgErrorType::InvalidInput,
});
}
};
Ok(Self {
pkg_name: name.to_string(),
})
}
#[cfg(test)]
fn from_str(source: &str, parser: Option<Parser>) -> Result<Self> {
let mut p = match parser {
Some(p) => p,
None => Default::default(),
};
let expr = p.parse_root_one(Cursor::new(source))?;
Self::from_expr(&expr)
}
fn gen_code_with_files(&self, template_files: &[impl AsRef<Path>]) -> Result<String> {
let mut tera = Tera::default();
let mut context = tera::Context::new();
let mut all_temps = vec![];
for p in template_files {
match p.as_ref().file_stem().map(|n| n.to_str()) {
Some(n) => {
all_temps.push((p, n));
}
None => (),
}
}
tera.add_template_files(all_temps)?;
context.insert("package_name", &self.pkg_name);
tera.render("Cargo.toml", &context)
.context("render def package wrong")
}
fn gen_code_with_tera(&self, templates: &Tera) -> Result<String> {
let mut context = tera::Context::new();
context.insert("package_name", &self.pkg_name);
templates
.render("Cargo.toml", &context)
.context("render def package wrong")
}
}
impl RPCSpec for DefPkg {
fn file_target(&self) -> TargetFile {
TargetFile::Cargo
}
fn symbol_name(&self) -> String {
self.pkg_name.clone()
}
fn as_cargo(&self) -> Option<&dyn RPCSpecCargo> {
Some(self)
}
}
impl RPCSpecCargo for DefPkg {
fn gen_code_with_temp_files(&self, temp_file_paths: &[String]) -> Result<String> {
self.gen_code_with_files(temp_file_paths)
}
fn gen_code_with_tera(&self, templates: &Tera) -> Result<String> {
self.gen_code_with_tera(templates)
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
#[test]
fn test_parse_def_pkg() {
let case = r#"(def-rpc-package demo)"#;
let dp = DefPkg::from_str(case, Default::default()).unwrap();
assert_eq!(
dp,
DefPkg {
pkg_name: "demo".to_string()
}
);
}
#[test]
fn test_gen_code() {
let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let template_file_path = vec![project_root.join("templates/Cargo.toml.template")];
let case = r#"(def-rpc-package demo)"#;
let dp = DefPkg::from_str(case, Default::default()).unwrap();
assert_eq!(
dp.gen_code_with_files(&template_file_path).unwrap(),
r#"[package]
name = "demo"
version = "0.1.0"
edition = "2024"
[dependencies]
actix-web = "4"
anyhow = "1"
lisp-rpc-rust-serializer = "0"
lisp-rpc-rust-server = "0"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
tracing = "0"
tracing-subscriber = { version = "0", features = ["env-filter"] }"#,
)
}
}