Skip to content

Commit

Permalink
✨ Add support for SQL message store
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurlm authored Nov 27, 2023
2 parents 6cbd82c + 54a3518 commit 15f2b2a
Show file tree
Hide file tree
Showing 17 changed files with 240 additions and 34 deletions.
30 changes: 28 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,48 @@ jobs:
with:
submodules: true
- uses: Swatinem/rust-cache@v2

- name: Build
run: cargo build --verbose
- name: Run tests no default features
run: cargo test --no-default-features
- name: Run tests
run: cargo test

lint:
test_mysql:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: Swatinem/rust-cache@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes libmysqlclient-dev
- name: Run tests
run: cargo test -F build-with-mysql

test_postgresql:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: Swatinem/rust-cache@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes libpq-dev
- name: Run tests
run: cargo test -F build-with-postgres

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: Swatinem/rust-cache@v2
- name: Lint
run: cargo clippy
- name: Format
Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "libquickfix"]
path = libquickfix
url = https://github.com/quickfix/quickfix.git
url = https://github.com/arthurlm/quickfix.git
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,24 @@ project(QuickFixBind)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/libquickfix/cmake/")

option(HAVE_MYSQL "Build with MySQL" OFF)
option(HAVE_POSTGRESQL "Build with PostgreSQL" OFF)

option(QUICKFIX_BIND_EXAMPLES "Build quickfix C binding examples" ON)
option(WITH_PRINT_EX_STDOUT, "Enable exception printing to stdout" OFF)

if (HAVE_MYSQL)
find_package(MySQL REQUIRED)
include_directories(${MYSQL_INCLUDE_DIR})
message("-- Building with MySQL")
endif()

if (HAVE_POSTGRESQL)
find_package(PostgreSQL REQUIRED)
include_directories(${PostgreSQL_INCLUDE_DIRS})
message("-- Building with POSTGRESQL")
endif()

add_subdirectory(quickfix-bind)
2 changes: 1 addition & 1 deletion libquickfix
12 changes: 10 additions & 2 deletions quickfix-bind/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,27 @@ include_directories(include)
add_library(quickfixbind STATIC
src/quickfix_bind.cpp
)
target_link_libraries(quickfixbind PRIVATE quickfix)
target_link_libraries(quickfixbind ${MYSQL_CLIENT_LIBS} ${PostgreSQL_LIBRARIES} quickfix)

# Add option if asked
if (WITH_PRINT_EX_STDOUT)
target_compile_definitions(quickfixbind PRIVATE "PRINT_QUICKFIX_EX_STDOUT=1")
endif()

if (HAVE_MYSQL)
target_compile_definitions(quickfixbind PRIVATE "HAVE_MYSQL=1")
endif()

if (HAVE_POSTGRESQL)
target_compile_definitions(quickfixbind PRIVATE "HAVE_POSTGRESQL=1")
endif()

# Add example if asked
if(QUICKFIX_BIND_EXAMPLES)
add_executable(demo_basic_binding
examples/demo_basic_binding.c
)
target_link_libraries(demo_basic_binding PRIVATE quickfixbind)
target_link_libraries(demo_basic_binding ${MYSQL_CLIENT_LIBS} ${PostgreSQL_LIBRARIES} quickfixbind)
endif()

# Configure install target
Expand Down
12 changes: 12 additions & 0 deletions quickfix-bind/include/quickfix_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#define PRINT_QUICKFIX_EX_STDOUT 0
#endif // PRINT_QUICKFIX_EX_STDOUT

// #define HAVE_MYSQL
// #define HAVE_POSTGRESQL

#define ERRNO_INVAL -1
#define ERRNO_EXCEPTION -2
#define ERRNO_BUFFER_TO_SMALL -3
Expand Down Expand Up @@ -76,6 +79,15 @@ void FixDataDictionary_delete(FixDataDictionary_t *obj);

FixMessageStoreFactory_t *FixFileMessageStoreFactory_new(const FixSessionSettings_t *settings);
FixMessageStoreFactory_t *FixMemoryMessageStoreFactory_new();

#ifdef HAVE_MYSQL
FixMessageStoreFactory_t *FixMysqlMessageStoreFactory_new(const FixSessionSettings_t *settings);
#endif // HAVE_MYSQL

#ifdef HAVE_POSTGRESQL
FixMessageStoreFactory_t *FixPostgresMessageStoreFactory_new(const FixSessionSettings_t *settings);
#endif // HAVE_POSTGRESQL

void FixMessageStoreFactory_delete(FixMessageStoreFactory_t *obj);

FixLogFactory_t *FixLogFactory_new(const void *data, const FixLogCallbacks_t *callbacks);
Expand Down
36 changes: 30 additions & 6 deletions quickfix-bind/src/quickfix_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
#include <quickfix/SocketAcceptor.h>
#include <quickfix/SocketInitiator.h>

#ifdef HAVE_MYSQL
#include <quickfix/MySQLStore.h>
#endif // HAVE_MYSQL

#ifdef HAVE_POSTGRESQL
#include <quickfix/PostgreSQLStore.h>
#endif // HAVE_POSTGRESQL

#define RETURN_IF_NULL(_OBJ_) \
if ((_OBJ_) == nullptr) \
return;
Expand Down Expand Up @@ -394,15 +402,31 @@ void FixDataDictionary_delete(FixDataDictionary_t *obj) {

FixMessageStoreFactory_t *FixFileMessageStoreFactory_new(const FixSessionSettings_t *settings) {
RETURN_VAL_IF_NULL(settings, NULL);
CATCH_OR_RETURN_NULL({
auto fix_settings = (FIX::SessionSettings *)(settings);
return (FixMessageStoreFactory_t *)(new FIX::FileStoreFactory(*fix_settings));
});

auto fix_settings = (FIX::SessionSettings *)(settings);
CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::FileStoreFactory(*fix_settings)); });
}

FixMessageStoreFactory_t *FixMemoryMessageStoreFactory_new() {
CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::MemoryStoreFactory()); })
FixMessageStoreFactory_t *FixMemoryMessageStoreFactory_new(){
CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::MemoryStoreFactory()); })}

#ifdef HAVE_MYSQL
FixMessageStoreFactory_t *FixMysqlMessageStoreFactory_new(const FixSessionSettings_t *settings) {
RETURN_VAL_IF_NULL(settings, NULL);

auto fix_settings = (FIX::SessionSettings *)(settings);
CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::MySQLStoreFactory(*fix_settings)); });
}
#endif // HAVE_MYSQL

#ifdef HAVE_POSTGRESQL
FixMessageStoreFactory_t *FixPostgresMessageStoreFactory_new(const FixSessionSettings_t *settings) {
RETURN_VAL_IF_NULL(settings, NULL);

auto fix_settings = (FIX::SessionSettings *)(settings);
CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::PostgreSQLStoreFactory(*fix_settings)); });
}
#endif // HAVE_POSTGRESQL

void FixMessageStoreFactory_delete(FixMessageStoreFactory_t *obj) {
RETURN_IF_NULL(obj);
Expand Down
2 changes: 2 additions & 0 deletions quickfix-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ cmake = "0.1.50"
[features]
default = []
print-ex = []
build-with-mysql = []
build-with-postgres = []
43 changes: 32 additions & 11 deletions quickfix-ffi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ use std::env;

use cmake::Config;

fn have_feature(flag: &str) -> bool {
env::var(&format!(
"CARGO_FEATURE_{}",
flag.replace('-', "_").to_uppercase()
))
.is_ok()
}

fn read_cmake_opt(flag: &str) -> &'static str {
if have_feature(flag) {
"ON"
} else {
"OFF"
}
}

fn main() {
// Tell Cargo that if the given file changes, to rerun this build script.
println!("cargo:rerun-if-changed=../CMakeLists.txt");
Expand All @@ -11,6 +27,9 @@ fn main() {

// Build quickfix as a static library
let quickfix_dst = Config::new("../libquickfix")
.define("HAVE_SSL", "OFF")
.define("HAVE_MYSQL", read_cmake_opt("build-with-mysql"))
.define("HAVE_POSTGRESQL", read_cmake_opt("build-with-postgres"))
.define("HAVE_PYTHON", "OFF")
.define("HAVE_PYTHON3", "OFF")
.define("QUICKFIX_SHARED_LIBS", "OFF")
Expand All @@ -24,19 +43,14 @@ fn main() {
// Build quickfix C bind also as a static library.
env::set_var("CMAKE_LIBRARY_PATH", [quickfix_lib_path].join(";"));

let mut config = Config::new("..");
config
let quickfix_bind_dst = Config::new("..")
.cflag(format!("-I{quickfix_include_path}"))
.cxxflag(format!("-I{quickfix_include_path}"))
.define("QUICKFIX_BIND_EXAMPLES", "OFF");

// Enable optional features
if env::var("CARGO_FEATURE_PRINT_EX").is_ok() {
config.define("WITH_PRINT_EX_STDOUT", "ON");
}

// Trigger build
let quickfix_bind_dst = config.build();
.define("QUICKFIX_BIND_EXAMPLES", "OFF")
.define("HAVE_MYSQL", read_cmake_opt("build-with-mysql"))
.define("HAVE_POSTGRESQL", read_cmake_opt("build-with-postgres"))
.define("WITH_PRINT_EX_STDOUT", read_cmake_opt("print-ex"))
.build();

// Configure rustc.
println!(
Expand All @@ -50,4 +64,11 @@ fn main() {
println!("cargo:rustc-link-lib=static=quickfix");
println!("cargo:rustc-link-lib=static=quickfixbind");
println!("cargo:rustc-link-lib=stdc++");

if have_feature("build-with-mysql") {
println!("cargo:rustc-link-lib=mysqlclient");
}
if have_feature("build-with-postgres") {
println!("cargo:rustc-link-lib=pq");
}
}
10 changes: 10 additions & 0 deletions quickfix-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ extern "C" {

pub fn FixMemoryMessageStoreFactory_new() -> Option<FixMessageStoreFactory_t>;

#[cfg(feature = "build-with-mysql")]
pub fn FixMysqlMessageStoreFactory_new(
settings: FixSessionSettings_t,
) -> Option<FixMessageStoreFactory_t>;

#[cfg(feature = "build-with-postgres")]
pub fn FixPostgresMessageStoreFactory_new(
settings: FixSessionSettings_t,
) -> Option<FixMessageStoreFactory_t>;

pub fn FixMessageStoreFactory_delete(obj: FixMessageStoreFactory_t);

// Log factory
Expand Down
2 changes: 2 additions & 0 deletions quickfix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ colored = "2.0.4"
[features]
default = ["log"]
print-ex = ["quickfix-ffi/print-ex"]
build-with-mysql = ["quickfix-ffi/build-with-mysql"]
build-with-postgres = ["quickfix-ffi/build-with-postgres"]
log = ["dep:log"]
4 changes: 4 additions & 0 deletions quickfix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub use trailer::Trailer;

#[cfg(feature = "log")]
pub use log_factory::RustLogger;
#[cfg(feature = "build-with-mysql")]
pub use message_store_factory::mysql::MySqlMessageStoreFactory;
#[cfg(feature = "build-with-postgres")]
pub use message_store_factory::postgres::PostgresMessageStoreFactory;

/// Permit control of an underlying socket connection.
pub trait ConnectionHandler {
Expand Down
6 changes: 6 additions & 0 deletions quickfix/src/message_store_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ use quickfix_ffi::{

use crate::{QuickFixError, SessionSettings};

#[cfg(feature = "build-with-mysql")]
pub mod mysql;

#[cfg(feature = "build-with-postgres")]
pub mod postgres;

/// Object can be converted as a foreign object representing a `MessageStore`.
pub trait FfiMessageStoreFactory {
/// Get a representation of the message store as a FFI pointer.
Expand Down
30 changes: 30 additions & 0 deletions quickfix/src/message_store_factory/mysql.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use quickfix_ffi::{
FixMessageStoreFactory_delete, FixMessageStoreFactory_t, FixMysqlMessageStoreFactory_new,
};

use crate::{FfiMessageStoreFactory, QuickFixError, SessionSettings};

/// MySQL based implementation of `MessageStore`.
#[derive(Debug)]
pub struct MySqlMessageStoreFactory(FixMessageStoreFactory_t);

impl MySqlMessageStoreFactory {
/// Try to create new struct from settings.
pub fn try_new(settings: &SessionSettings) -> Result<Self, QuickFixError> {
unsafe { FixMysqlMessageStoreFactory_new(settings.0) }
.map(Self)
.ok_or(QuickFixError::NullFunctionReturn)
}
}

impl FfiMessageStoreFactory for MySqlMessageStoreFactory {
fn as_ffi_ptr(&self) -> FixMessageStoreFactory_t {
self.0
}
}

impl Drop for MySqlMessageStoreFactory {
fn drop(&mut self) {
unsafe { FixMessageStoreFactory_delete(self.0) }
}
}
30 changes: 30 additions & 0 deletions quickfix/src/message_store_factory/postgres.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use quickfix_ffi::{
FixMessageStoreFactory_delete, FixMessageStoreFactory_t, FixPostgresMessageStoreFactory_new,
};

use crate::{FfiMessageStoreFactory, QuickFixError, SessionSettings};

/// PostgreSQL based implementation of `MessageStore`.
#[derive(Debug)]
pub struct PostgresMessageStoreFactory(FixMessageStoreFactory_t);

impl PostgresMessageStoreFactory {
/// Try to create new struct from settings.
pub fn try_new(settings: &SessionSettings) -> Result<Self, QuickFixError> {
unsafe { FixPostgresMessageStoreFactory_new(settings.0) }
.map(Self)
.ok_or(QuickFixError::NullFunctionReturn)
}
}

impl FfiMessageStoreFactory for PostgresMessageStoreFactory {
fn as_ffi_ptr(&self) -> FixMessageStoreFactory_t {
self.0
}
}

impl Drop for PostgresMessageStoreFactory {
fn drop(&mut self) {
unsafe { FixMessageStoreFactory_delete(self.0) }
}
}
11 changes: 0 additions & 11 deletions quickfix/tests/test_lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,3 @@ fn test_extra_log_factory() {

let _file_log_factory = LogFactory::try_new(&RustLogger).unwrap();
}

#[test]
fn test_file_message_store_factory() {
let settings = SessionSettings::try_from_path("../configs/settings.ini").unwrap();
let _message_store_factory = FileMessageStoreFactory::try_new(&settings).unwrap();
}

#[test]
fn test_memory_message_store_factory() {
let _message_store_factory = MemoryMessageStoreFactory::new();
}
Loading

0 comments on commit 15f2b2a

Please sign in to comment.