use std::{fs::File, io::Write, path::Path};
use clang::Entity;
use crate::{
convert_spi_event_enum_, convert_spi_event_struct_,
setting::{Config, ModuleFlavor, PointerStyle},
traverse_ast,
};
use super::{
rustify_method_name2,
setting::{Context, ItemVec, ParamVec},
};
pub fn generate_stream_wrapper_code<P: AsRef<Path>>(e: &Entity, target_dir: P) {
let mut cfg = Config::for_spi();
cfg.source_class_name = "CThostFtdcMdSpi".to_string();
cfg.generate_trait_name = "MdSpi".to_string();
cfg.wrap_spi_trait = "MdSpi".to_string();
let mut ctx = Context::for_spi(cfg);
let mut cfg = Config::for_spi();
cfg.source_class_name = "CThostFtdcTraderSpi".to_string();
cfg.generate_trait_name = "TraderSpi".to_string();
cfg.wrap_spi_trait = "TraderSpi".to_string();
ctx.cfg = cfg;
let spi_event_code = prepare_spi_event_code(e);
let mut file = File::create(target_dir.as_ref().join("event.rs")).expect("create file error");
file.write_all(spi_event_code.as_bytes())
.expect("write code failed!");
let stream_spi_code = prepare_stream_spi_code(e);
let mut file = File::create(target_dir.as_ref().join("stream.rs")).expect("create file error");
file.write_all(stream_spi_code.as_bytes())
.expect("write code failed!");
}
pub fn prepare_spi_event_code(e: &Entity) -> String {
let mut cfg = Config::for_spi();
cfg.source_class_name = "CThostFtdcMdSpi".to_string();
cfg.generate_trait_name = "MdSpi".to_string();
cfg.wrap_spi_trait = "MdSpi".to_string();
let mut ctx = Context::for_spi(cfg);
ctx.cfg = Config {
module_flavor: ModuleFlavor::EventEnum,
prefer_pointer: PointerStyle::RawPointerConst,
prefer_param_cffi_type: true,
param_trim_prefix: false,
..ctx.cfg.clone()
};
ctx.methods
.insert("_".to_string(), Box::new(convert_spi_event_enum_));
let code_0 = traverse_ast(&mut ctx, e);
ctx.cfg = Config {
module_flavor: ModuleFlavor::EventEnumStruct,
prefer_pointer: PointerStyle::RawPointerConst,
prefer_param_cffi_type: false,
param_trim_prefix: true,
..ctx.cfg.clone()
};
ctx.methods
.insert("_".to_string(), Box::new(convert_spi_event_struct_));
let code_1 = traverse_ast(&mut ctx, e);
ctx.cfg = Config {
source_class_name: "CThostFtdcTraderSpi".to_string(),
generate_trait_name: "TraderSpi".to_string(),
wrap_spi_trait: "TraderSpi".to_string(),
module_flavor: ModuleFlavor::EventEnum,
prefer_pointer: PointerStyle::RawPointerConst,
prefer_param_cffi_type: false,
param_trim_prefix: true,
..ctx.cfg.clone()
};
ctx.methods
.insert("_".to_string(), Box::new(convert_spi_event_enum_));
let code_2 = traverse_ast(&mut ctx, e);
ctx.cfg = Config {
module_flavor: ModuleFlavor::EventEnumStruct,
prefer_pointer: PointerStyle::RawPointerConst,
prefer_param_cffi_type: false,
param_trim_prefix: true,
..ctx.cfg.clone()
};
ctx.methods
.insert("_".to_string(), Box::new(convert_spi_event_struct_));
let code_3 = traverse_ast(&mut ctx, e);
let version = &ctx.cfg.version;
format!(
r#"
use crate::{version}::bindings::*;
{code_0}
{code_1}
{code_2}
{code_3}
"#
)
}
pub fn prepare_stream_spi_code(e: &Entity) -> String {
let mut cfg = Config::for_spi();
cfg.source_class_name = "CThostFtdcMdSpi".to_string();
cfg.generate_trait_name = "MdSpi".to_string();
cfg.wrap_spi_trait = "MdSpi".to_string();
let mut ctx = Context::for_spi(cfg);
ctx.cfg = Config {
module_flavor: ModuleFlavor::SpiFn,
prefer_pointer: PointerStyle::RawPointerConst,
prefer_param_cffi_type: false,
param_trim_prefix: true,
..ctx.cfg.clone()
};
ctx.methods
.insert("_".to_string(), Box::new(convert_stream_spi_trait_func_));
ctx.modules
.insert(ModuleFlavor::SpiFn, Box::new(handle_module_spi_stream));
let code_0 = traverse_ast(&mut ctx, e);
ctx.cfg = Config {
source_class_name: "CThostFtdcTraderSpi".to_string(),
generate_trait_name: "TraderSpi".to_string(),
wrap_spi_trait: "TraderSpi".to_string(),
module_flavor: ModuleFlavor::SpiFn,
prefer_pointer: PointerStyle::RawPointerConst,
prefer_param_cffi_type: false,
param_trim_prefix: true,
..ctx.cfg.clone()
};
let code_1 = traverse_ast(&mut ctx, e);
let version = &ctx.cfg.version;
let header = format!(
r#"
use futures::stream::Stream;
use std::{{
pin::Pin,
sync::{{Arc, Mutex}},
task::Waker,
}};
use crate::{version}::bindings::*;
use crate::{version}::event::*;
use crate::{version}::mdspi::*;
use crate::{version}::traderspi::*;
"#
);
format!(
r#"{header}
{code_0}
{code_1}
"#
)
}
pub fn handle_module_spi_stream(ctx: &Context, e: &Entity, items: &ItemVec) -> String {
let spi_trait = &ctx.cfg.generate_trait_name;
let declare_code = format!(
r#"
struct {spi_trait}Inner {{
buf: std::collections::VecDeque<{spi_trait}Event>,
waker: Option<Waker>,
}}
impl {spi_trait}Inner {{
fn push(&mut self, msg: {spi_trait}Event) {{
self.buf.push_back(msg);
if let Some(waker) = self.waker.take() {{
waker.wake()
}}
}}
}}
pub struct {spi_trait}Stream {{
inner: Arc<Mutex<{spi_trait}Inner>>,
}}
impl Stream for {spi_trait}Stream {{
type Item = {spi_trait}Event;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut futures::task::Context<'_>,
) -> futures::task::Poll<Option<Self::Item>> {{
use futures::task::Poll;
let mut inner = self.inner.lock().unwrap();
if let Some(i) = inner.buf.pop_front() {{
Poll::Ready(Some(i))
}} else {{
inner.waker = Some(cx.waker().clone());
Poll::Pending
}}
}}
fn size_hint(&self) -> (usize, Option<usize>) {{
(0, None)
}}
}}"#
);
format!(
r#"{declare_code}
impl {spi_trait} for {spi_trait}Stream {{
{spi_code}
}}
"#,
spi_code = items.join("\n")
)
}
macro_rules! indent {
($width:expr) => {
" ".repeat($width)
};
}
pub fn convert_stream_spi_trait_func_(ctx: &Context, e: &Entity, params: &ParamVec) -> String {
let method_call = e.get_name().unwrap();
let method_name = match ctx.cfg.method_to_snake {
true => rustify_method_name2(&method_call),
false => method_call.clone(),
};
let spi_trait = &ctx.cfg.generate_trait_name;
let mut arg_vec = params
.iter()
.map(|x| format!("{}: {}", x.0, x.1))
.collect::<Vec<String>>();
if ctx.cfg.prefer_self_mut_ref {
arg_vec.insert(0, "&mut self".to_string());
} else {
arg_vec.insert(0, "&self".to_string());
}
let field_code = params
.iter()
.map(|x| format!("{}{}: {}", indent!(16), x.0, x.2))
.collect::<Vec<_>>()
.join(",\n");
let trait_method_code = format!(
r#"
fn {method_name}({arg_list}) {{
self.inner.lock().unwrap().push({spi_trait}Event::{method_call}(
{spi_trait}{method_call}Event {{
{field_code}
}}
))
}}"#,
arg_list = arg_vec.join(", "),
);
trait_method_code
}