TVF
Tag Value Field is the internal message interface used by ProSA.
It’s not a full-fledged format but a Rust trait that defines what a message format should support.
Currently, only the SimpleStringTvf implementation exists. However, in the future, others could implement the TVF trait, such as ProtoBuf, and more.
Usage
In ProSA, Tvf is used as a generic message type that can support various serialization strategies.
The trait allows you to:
- Add fields using
put_*methods - Retrieve fields using
get_*methods - Access information from the container
- …
Most of the time, when using a component that use TVF, you’ll see a generic declaration like:
struct StructObject<M>
where
M: 'static
+ std::marker::Send
+ std::marker::Sync
+ std::marker::Sized
+ std::clone::Clone
+ std::fmt::Debug
+ prosa_utils::msg::tvf::Tvf
+ std::default::Default,
{
fn create_tvf() -> M {
let buffer = M::default();
buffer.put_string(1, "value");
println!("TVF contains: {buffer:?}");
buffer
}
}
To create a TVF, the
Defaulttrait must be implemented.
Good to have are
CloneandDebugfor your TVF. When TVFs are used for messaging,SendandSyncare essential to safely move them across threads.
Constructing TVF Messages with the tvf! Macro
Instead of manually calling put_* methods one by one, you can use the tvf! procedural macro to construct TVF messages with a concise syntax.
Basic usage (map)
Create a TVF with key-value pairs using =>:
use prosa_macros::tvf;
use prosa_utils::msg::simple_string_tvf::SimpleStringTvf;
let message = tvf!(SimpleStringTvf {
1 => "hello",
2 => 42,
3 => 3.14,
});
Integer values are inferred as signed (i64) by default. Use type suffixes or as casts to specify other types:
let message = tvf!(SimpleStringTvf {
1 => 2, // signed (i64)
2 => 4usize, // also signed
3 => 1 as Unsigned, // unsigned (u64)
4 => 2.5 as Float, // float (f64)
});
Type annotations
Use as Type to explicitly set the field type:
| Annotation | Rust type | Example |
|---|---|---|
as Unsigned | u64 | 1 as Unsigned |
as Signed | i64 | -5 as Signed |
as Float | f64 | 3.14 as Float |
as Byte | u8 | 22 as Byte |
as Bytes | Bytes | 0x01020304 as Bytes |
as Date | NaiveDate | "1995-01-10" as Date |
as DateTime | NaiveDateTime | "2023-06-05 15:02:00.000" as DateTime |
Lists
Use [ ] to create sequential fields indexed starting from 1:
let list = tvf!(SimpleStringTvf [
"first element", // index 1
"second element", // index 2
42, // index 3
]);
Nested buffers
Nest TVF structures inside other TVF messages:
let message = tvf!(SimpleStringTvf {
1 => 2,
5 => [
1 as Unsigned,
2 as Float,
3,
"four",
5 as Byte,
{
1 => "nested object",
2 => 0x00010203 as Bytes,
}
],
6 => "1995-01-10" as Date,
200 => "2023-06-05 15:02:00.000" as DateTime,
});
Lists can contain nested maps ({ }), and maps can contain nested lists ([ ]), allowing arbitrary depth.
Implement your own TVF
If you have your own internal format, you can implement the TVF trait on your own and expose your TVF struct:
impl Tvf for MyOwnTvf {
// All trait method must be implement here
}
Make sure to also implement:
Default: to create an empty or initial TVFSend/Sync: to safely transfer across threadsClone: if duplication of buffers is neededDebug: To enable easy debugging and inspection
Declare your custom TVF
When you implement your own TVF, you need to expose it in your Cargo.toml metadata as discussed in the previous chapter.
To do this, add the following to your Cargo.toml file:
[package.metadata.prosa]
tvf = ["tvf::MyOwnTvf"]
Be sure to specify the entire path of your implementation, tvf::MyOwnTvf, in this case, if you place it in src/tvf.rs.
Handling sensitive data
At Worldline, since we process payments, messages may contain sensitive data. This data must not be printed or extracted from the application to ensure security.
To address this, ProSA provides the TvfFilter trait, which allows filtering and masking sensitive fields.
Depending on your message, sensitive field may vary.
Since TvfFilter is a trait, you can implement your own filter tailored to your message format.