ctp-dyn 0.1.8

sfit ctp & ctp-mini & ctp-sopt rust native binding with dynlib libloading support, compatible with Linux and macOS
Documentation
#![allow(unused_variables, unused_macros)]
use std::env::var;

use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use clang::*;

mod codegen;
use codegen::*;

macro_rules! p {
    ($($tokens: tt)*) => {
        println!("cargo::warning={}", format!($($tokens)*))
    }
}
fn get_sdk_path() -> &'static std::path::Path {
    // 基于版本的分支判断

    if cfg!(feature = "mini_v1_6_9") {
        return Path::new("./api/mini/v1.6.9/CTPMini_V1.6.9_linux64_api_20240527/");
    }

    if cfg!(feature = "mini_v1_7_0") {
        return Path::new("./api/mini/v1.7.0/CTPIIMini_V1.7.0_linux64_api_20240923/");
    }

    if cfg!(feature = "sopt_v3_7_3") {
        return Path::new("./api/ctpsopt/v3.7.3/v3.7.3_20240910_api_traderapi_linux64_se/");
    }

    if cfg!(feature = "ctp_v6_5_1") {
        if cfg!(feature = "openctp") {
            return Path::new(
                "./api/ctp/v6.5.1/v6.5.1_CP_20200904_20200904_api_tradeapi_se_linux64",
            );
        }
        if cfg!(target_os = "macos") {
            panic!("`macOS platform` not supported for `v6_5_1`.");
        }
        if cfg!(target_os = "linux") {
            return Path::new(
                "./api/ctp/v6.5.1/v6.5.1_CP_20200904_20200904_api_tradeapi_se_linux64",
            );
        }
    }
    if cfg!(feature = "ctp_v6_7_7") {
        if cfg!(feature = "openctp") {
            panic!("`openctp` feature not supported for `v6_7_7`.");
        }
        if cfg!(target_os = "macos") {
            return Path::new("./api/ctp/v6.7.7/v6.7.7_MacOS_20240716");
        }
        if cfg!(target_os = "linux") {
            return Path::new("./api/ctp/v6.7.7/v6.7.7_20240607_api_traderapi_se_linux64");
        }
    }

    if cfg!(feature = "ctp_v6_7_8") {
        if cfg!(feature = "openctp") {
            panic!("`openctp` feature not supported for `v6_7_8`.");
        }
        if cfg!(target_os = "macos") {
            panic!("`macOS platform` not supported for `v6_7_8`.");
        }
        if cfg!(target_os = "linux") {
            return Path::new("./api/ctp/v6.7.8/v6.7.8_20240918_api_traderapi_se_linux64");
        }
    }

    if cfg!(feature = "ctp_v6_7_9") {
        if cfg!(feature = "openctp") {
            panic!("`openctp` feature not supported for `v6_7_9`.");
        }
        if cfg!(target_os = "macos") {
            panic!("`macOS platform` not supported for `v6_7_9`.");
        }
        if cfg!(target_os = "linux") {
            return Path::new("./api/ctp/v6.7.9/v6.7.9_P1_20250319_api_traderapi_se_linux64/");
        }
    }

    if cfg!(feature = "ctp_v6_7_2") {
        if cfg!(feature = "openctp") {
            return Path::new("./api/ctp/v6.7.2/v6.7.2_20230913_api_traderapi_se_linux64");
        }
        if cfg!(target_os = "macos") {
            return Path::new("./api/ctp/v6.7.2/v6.7.2_MacOS_20231016");
        }
        if cfg!(target_os = "linux") {
            return Path::new("./api/ctp/v6.7.2/v6.7.2_20230913_api_traderapi_se_linux64");
        }
    }

    panic!("Either 'ctp_v6_7_2' or 'ctp_v6_7_7' feature must be enabled.");
}

fn ensure_dir_exists(path: &PathBuf) -> io::Result<()> {
    if !path.exists() {
        // 如果目录不存在,创建它(包括父目录)
        fs::create_dir_all(path)?;
    }
    Ok(())
}

fn build_dyn() {
    #[cfg(feature = "v1alpha1")]
    let version = "v1alpha1";

    #[cfg(feature = "v1alpha2")]
    let version: &str = "v1alpha2";

    let base_dir = var("CARGO_MANIFEST_DIR").unwrap();
    let source_dir = Path::new(&base_dir).join("src");
    let generate_dir = source_dir.join(version);
    let _ = ensure_dir_exists(&generate_dir);

    let sdk_path = get_sdk_path();
    let include_arg = format!("-I{}", sdk_path.to_string_lossy());
    p!("include: {}", include_arg);

    println!("cargo:rerun-if-changed=src/wrapper.hpp");
    println!("cargo:rerun-if-changed=build.rs");

    clang_sys::load().expect("");
    let clang = Clang::new().unwrap();
    let index = Index::new(&clang, false, false);

    let file_path = Path::new("src/wrapper.hpp");
    let tu = index
        .parser(file_path)
        .arguments(&[include_arg.as_str()])
        .parse()
        .unwrap();

    let out_path = PathBuf::from(var("OUT_DIR").unwrap());
    generate_mduser_wrapper_code(&tu.get_entity(), &out_path);
    generate_trader_wrapper_code(&tu.get_entity(), &out_path);
    generate_stream_wrapper_code(&tu.get_entity(), &out_path);

    if let Err(err) = generate_errors_wrapper_code(sdk_path.join("error.xml"), &out_path) {
        panic!("Failed to generate errors wrapper code: {}", err);
    }

    let bindings = bindgen::Builder::default()
        .header("src/wrapper.hpp")
        .clang_arg(include_arg)
        .clang_arg("-x")
        .clang_arg("c++")
        .rustified_enum(".*")
        .vtable_generation(true)
        .disable_name_namespacing()
        .derive_default(true)
        .derive_debug(true)
        .derive_copy(true)
        .ignore_methods()
        .ignore_functions()
        .derive_hash(false)
        .layout_tests(false)
        .generate_comments(true)
        .generate()
        .expect("Unable to generate bindings");

    let binding_file = out_path.join("bindings.rs");
    bindings
        .write_to_file(&binding_file)
        .expect("Couldn't write bindings!");
}

fn main() {
    build_dyn();
}