1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2016 James Bendig. See the COPYRIGHT file at the top-level
// directory of this distribution.
//
// Licensed under:
//   the MIT license
//     <LICENSE-MIT or https://opensource.org/licenses/MIT>
//   or the Apache License, Version 2.0
//     <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>,
// at your option. This file may not be copied, modified, or distributed
// except according to those terms.

use field_tag::FieldTag;
use field_type::FieldType;
use fix_version::FIXVersion;
use message_version::MessageVersion;
use rule::Rule;

pub trait Field {
    type Type;
    fn rule() -> Rule;
    fn tag_bytes() -> &'static [u8];
    fn tag() -> FieldTag;
    fn read(field: &<<Self as Field>::Type as FieldType>::Type,fix_version: FIXVersion,message_version: MessageVersion,buf: &mut Vec<u8>,required: bool) -> usize
        where <Self as Field>::Type: FieldType;
}

#[macro_export]
macro_rules! define_fields {
    ( $( $field_name:ident : $field_type:ty = $tag:expr $( => $rule:expr )* ),* $(),* ) => { $(
        #[derive(BuildField)]
        pub struct $field_name {
            #[tag=$tag]
            _tag_gen: ::std::marker::PhantomData<()>,
        }

        impl $crate::field::Field for $field_name {
            type Type = $field_type;

            #[allow(unreachable_code)]
            fn rule() -> $crate::rule::Rule {
                //If a rule is provided, prefer it first.
                $(
                    return $rule //A maximum of one rule may be specified.
                )*;

                //Next, check if the field type provides a rule. This way the BeginGroup rule
                //can be specified automatically instead of using a nasty boilerplate in each field
                //definition.
                if let Some(rule) = <$field_type as $crate::field_type::FieldType>::rule() {
                    rule
                }
                //Otherwise, no rule was specified.
                else {
                    $crate::rule::Rule::Nothing
                }
            }

            fn tag_bytes() -> &'static [u8] {
                Self::tag_bytes()
            }

            fn tag() -> $crate::field_tag::FieldTag {
                Self::tag()
            }

            fn read(field: &<<Self as $crate::field::Field>::Type as $crate::field_type::FieldType>::Type,fix_version: $crate::fix_version::FIXVersion,message_version: $crate::message_version::MessageVersion,buf: &mut Vec<u8>,required: bool) -> usize {
                use ::std::io::Write;

                if !required && <$field_type as $crate::field_type::FieldType>::is_empty(field) {
                    return 0;
                }

                let mut result = 1;

                match <$field_name as $crate::field::Field>::rule() {
                    //If this is the first part of a Rule::PrepareForBytes and Rule::ConfirmPreviousTag
                    //pair, skip the tag completely.
                    $crate::rule::Rule::PrepareForBytes{ .. } => {
                        return 0;
                    },
                    //If this is the second part of a Rule::PrepareForBytes and
                    //Rule::ConfirmPreviousTag pair, insert the length tag first.
                    $crate::rule::Rule::ConfirmPreviousTag{ previous_tag } => {
                        let previous_tag = previous_tag.to_bytes();
                        result += 2;
                        result += buf.write(&previous_tag[..]).unwrap();
                        buf.push($crate::constant::TAG_END);
                        result += buf.write(<$field_type as $crate::field_type::FieldType>::len(field).to_string().as_bytes()).unwrap();
                        buf.push($crate::constant::VALUE_END);
                    },
                    //If this tag should only be serialized with a different FIX version, skip the
                    //tag completely.
                    $crate::rule::Rule::RequiresFIXVersion{ fix_version: required_fix_version } => {
                        if fix_version != required_fix_version {
                            return 0;
                        }
                    },
                    _ => {},
                };

                //Write tag and value.
                result += buf.write(Self::tag_bytes()).unwrap();
                buf.push($crate::constant::TAG_END);
                result += <$field_type as $crate::field_type::FieldType>::read(field,fix_version,message_version,buf);

                //Avoid the VALUE_END symbol iff this is not a repeating group field. This is a
                //hack, under the assumption that the field itself adds this symbol, so the field
                //can append the remaining groups.
                if let $crate::rule::Rule::BeginGroup{ .. } = <$field_name as $crate::field::Field>::rule() {}
                else {
                    result += 1;
                    buf.push($crate::constant::VALUE_END);
                }

                result
            }
        }
    )*};
}