fix-rs 0.2.1

FIX (Financial Information Exchange) engine written in Rust.
Documentation
// Public domain, 2017-02-21, James Bendig.

#![allow(unused_variables)]

#[macro_use]
extern crate fix_rs;

use std::time::Duration;

use fix_rs::dictionary::messages::{BusinessMessageReject,Heartbeat,Logon,Logout,Reject,ResendRequest,SequenceReset,TestRequest};
use fix_rs::fixt::engine::{Engine,EngineEvent,ResendResponse};

fn main() {
    //List only the messages we need. The define_dictionary!() macro creates the following for us:
    //  fn build_dictionary(): A function that turns the listed messages into instructions used for
    //                         parsing FIX messages.
    //  enum MessageEnum:      An enum of the listed messages so Rust's match statement can be used to
    //                         make sure all messages are being handled.
    //  fn message_to_enum():  A function used to convert a FIXTMessage into a MessageEnum. Since
    //                         the Engine returns FIXTMessages, this function is a convenient way
    //                         to match and get concrete types.
    define_dictionary!(
        BusinessMessageReject,
        Heartbeat,
        Logon,
        Logout,
        Reject,
        ResendRequest,
        SequenceReset,
        TestRequest,
    );

    //Create an Engine which is used for accepting FIX connections.
    let max_message_size = 4096; //The maximum message size allowed to be received in bytes.
    let mut server = Engine::new(build_dictionary(),max_message_size).unwrap();

    //Setup a listener to accept new connections. The listener_id is used to interact with this
    //listener.
    let sender_comp_id = b"Server"; //SenderCompID sent in every FIX message.
    let addr = "127.0.0.1:7001"; //IP and port to listen on.
    let listener_id = server.add_listener(sender_comp_id,addr);

    //Poll server for new events. Events include new connections, connection status updates,
    //received messages, errors, etc.
    let timeout_duration = Duration::from_secs(120); //Optional
    while let Some(event) = server.poll(timeout_duration) {
        match event {
            //Listener accepted a new connection and is awaiting a Logon message.
            EngineEvent::ConnectionAccepted(listener_id,connection_id,addr) => {
                println!("({},{})New connection accepted: {}",listener_id,connection_id,addr);

                //The connection can be rejected here if the addr is blacklisted/not whitelisted or
                //if over capacity.
                //listener.reject_new_connection(connection_id,None);
            },
            //Listener accepted a new connection but doesn't have the capacity to manage it so it
            //was immediately dropped.
            EngineEvent::ConnectionDropped(listener_id,addr) => {
                println!("({})New connection was dropped: {}",listener_id,addr);
            },
            //Connection sent a Logon message and is awaiting approval or rejection.
            EngineEvent::ConnectionLoggingOn(listener_id,connection_id,logon) => {
                if logon.username == b"some_user" &&
                   logon.password == b"some_password" {
                    let mut response_logon = Logon::new();
                    response_logon.encrypt_method = logon.encrypt_method.clone();
                    response_logon.heart_bt_int = logon.heart_bt_int.clone();
                    response_logon.default_appl_ver_id = logon.default_appl_ver_id;
                    server.approve_new_connection(connection_id,Box::new(response_logon),None);

                    //Store connection_id somewhere to interact with this connection in the future.
                }
                else {
                    server.reject_new_connection(connection_id,Some(b"Invalid username and/or password".to_vec()));
                }
            },
            //Listener could not be setup because of a lack of resources.
            EngineEvent::ListenerFailed(listener_id,err) => {
                println!("({})Listener failed: {:?}",listener_id,err);
                break;
            },
            //Listener encountered a socket error while trying to accept a new connection.
            EngineEvent::ListenerAcceptFailed(listener_id,err) => {
                println!("({})Listener accept failed: {:?}",listener_id,err);
                break;
            }
            //Connection to server was closed either using the Engine::logout() function, logout
            //request by client, or an unrecoverable error.
            EngineEvent::ConnectionTerminated(connection_id,reason) => {
                println!("({})Connection terminated: {:?}",connection_id,reason);
            },
            //Connection received a new message.
            EngineEvent::MessageReceived(connection_id,message) => {
                //Handle the received message. Must be one of the messages listed in the
                //define_dictionary!() macro above. In this case, these are all administrative
                //messages that are handled by the Engine automatically but are passed along here
                //for logging purposes.
                match message_to_enum(&*message) {
                    MessageEnum::BusinessMessageReject(message) => {},
                    MessageEnum::Heartbeat(message) => {},
                    MessageEnum::Logon(message) => {},
                    MessageEnum::Logout(message) => {},
                    MessageEnum::Reject(message) => {},
                    MessageEnum::ResendRequest(message) => {},
                    MessageEnum::SequenceReset(message) => {},
                    MessageEnum::TestRequest(message) => {},
                };
            },
            //Connection received a message that could not be parsed correctly.
            EngineEvent::MessageReceivedGarbled(connection_id,parse_error) => {
                println!("({})Could not parse message: {}",connection_id,parse_error);
            },
            //Connection received a message with a MsgSeqNum matching another message that was
            //already received.
            EngineEvent::MessageReceivedDuplicate(connection_id,message) => {
                println!("({})Received message with duplicate MsgSeqNum: {}",connection_id,message.msg_seq_num());
            },
            //Connection received a message that doesn't follow session rules and was rejected. No
            //further action is necessary but it might be worth logging.
            EngineEvent::MessageRejected(connection_id,message) => {
                println!("({})Message was rejected",connection_id);
            },
            //Connected received a ResendRequest message for the messages in
            //[range.start,range.end).
            EngineEvent::ResendRequested(connection_id,range) => {
                //Messages should be added to response in order. Administrative messages should be
                //filled with a ResendResponse::Gap. Business messages should each be added as a
                //ResendResponse::Message.
                let mut response = Vec::new();
                response.push(ResendResponse::Gap(range));
                server.send_resend_response(connection_id,response);
            },
            //Connection received a SequenceReset-Reset message where NewSeqNo is set to the same
            //number as the expected MsgSeqNum.
            EngineEvent::SequenceResetResetHasNoEffect(connection_id) => {
                println!("({})Received SequenceReset-Reset with no effect",connection_id);
            },
            //Connection received a SequenceReset-Reset message where NewSeqNo is set to an already
            //seen MsgSeqNum.
            EngineEvent::SequenceResetResetInThePast(connection_id) => {
                println!("({})Received SequenceReset-Reset where NoSeqNo is in the past",connection_id);
            },
            //Internal error setting up Engine (before any listeners were added).
            EngineEvent::FatalError(_,_) => {
                println!("Could not setup Engine.");
                break;
            },
            //The following events are not used for connections accepted using a listener.
            //Connection was able to open TCP stream to server.
            EngineEvent::ConnectionSucceeded(_) |
            EngineEvent::ConnectionFailed(_,_) |
            EngineEvent::SessionEstablished(_) => {},
        }
    }
}