1use crate::fintekkers::models::portfolio::PortfolioProto;
2use crate::fintekkers::models::position::{
3 field_map_entry, FieldMapEntry, FieldProto, MeasureMapEntry, MeasureProto, PositionFilterProto,
4 PositionProto,
5};
6use crate::fintekkers::models::price::PriceProto;
7use crate::fintekkers::models::security::{IdentifierProto, SecurityProto, TenorProto};
8use crate::fintekkers::models::strategy::StrategyProto;
9use crate::fintekkers::models::util::{LocalDateProto, LocalTimestampProto, UuidProto};
10use crate::fintekkers::wrappers::models::portfolio::PortfolioWrapper;
11use crate::fintekkers::wrappers::models::security::SecurityWrapper;
12use crate::fintekkers::wrappers::models::utils::errors::Error;
13use crate::fintekkers::wrappers::models::utils::serialization::ProtoSerializationUtil;
14use chrono::naive::NaiveDate;
15use chrono::DateTime;
16use chrono_tz::Tz;
17use prost::Message;
18use prost_types::Any;
19use rust_decimal::Decimal;
20use uuid::Uuid;
21
22#[derive(Debug)]
23pub struct Position {
24 position_proto: PositionProto,
25}
26
27impl Position {
28 pub fn new(position_proto: PositionProto) -> Position {
29 Position { position_proto }
30 }
31
32 pub fn get_field_value(
33 &self,
34 field: FieldProto,
35 ) -> Result<Box<dyn std::any::Any>, &'static str> {
36 let field_entry = FieldMapEntry {
37 field: field as i32,
38 operator: 0,
39 field_map_value_one_of: None,
40 };
41 self.get_field(field_entry)
42 }
43
44 pub fn get_field(
45 &self,
46 field_to_get: FieldMapEntry,
47 ) -> Result<Box<dyn std::any::Any>, &'static str> {
48 for tmp_field in &self.position_proto.fields {
49 if tmp_field.field == field_to_get.field {
50 if let Some(field_map_entry::FieldMapValueOneOf::StringValue(ref s)) =
52 tmp_field.field_map_value_one_of
53 {
54 if !s.is_empty() {
55 return Ok(Box::new(s.clone()));
56 }
57 }
58
59 if let Some(field_map_entry::FieldMapValueOneOf::EnumValue(enum_val)) =
61 tmp_field.field_map_value_one_of
62 {
63 return Ok(Box::new(enum_val));
64 }
65
66 if let Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(_)) =
68 tmp_field.field_map_value_one_of
69 {
70 let field_enum = FieldProto::from_i32(field_to_get.field)
71 .ok_or("Invalid field enum value")?;
72
73 if field_enum == FieldProto::Portfolio {
75 if let Ok(proto) =
76 Self::unpack_field(tmp_field)?.downcast::<PortfolioProto>()
77 {
78 return Ok(Box::new(PortfolioWrapper::new(*proto)));
79 }
80 }
81
82 if field_enum == FieldProto::Security
83 || field_enum == FieldProto::CashImpactSecurity
84 {
85 if let Ok(proto) =
86 Self::unpack_field(tmp_field)?.downcast::<SecurityProto>()
87 {
88 return Ok(Box::new(SecurityWrapper::new(*proto)));
89 }
90 }
91
92 return Self::unpack_field(tmp_field);
94 }
95 }
96 }
97 Err("Could not find field in position")
98 }
99
100 pub fn get_measure_value(&self, measure: MeasureProto) -> Result<Decimal, &'static str> {
101 let measure_entry = MeasureMapEntry {
102 measure: measure as i32,
103 measure_decimal_value: None,
104 };
105 self.get_measure(measure_entry)
106 }
107
108 pub fn get_measure(&self, measure_to_get: MeasureMapEntry) -> Result<Decimal, &'static str> {
109 for tmp_measure in &self.position_proto.measures {
110 if tmp_measure.measure == measure_to_get.measure {
111 return Self::unpack_measure(tmp_measure);
112 }
113 }
114 Err("Could not find measure in position")
115 }
116
117 pub fn get_field_display(&self, field_to_get: FieldMapEntry) -> Result<String, &'static str> {
118 let value = self.get_field(field_to_get)?;
119
120 if let Some(s) = value.downcast_ref::<String>() {
122 return Ok(s.clone());
123 }
124
125 if let Some(i) = value.downcast_ref::<i32>() {
126 return Ok(i.to_string());
127 }
128
129 if let Some(portfolio) = value.downcast_ref::<PortfolioWrapper>() {
131 return Ok(portfolio.portfolio_name().to_string());
132 }
133
134 if let Some(_security) = value.downcast_ref::<SecurityWrapper>() {
135 return Ok("Security".to_string());
137 }
138
139 if let Some(uuid) = value.downcast_ref::<Uuid>() {
140 return Ok(uuid.to_string());
141 }
142
143 if let Some(date) = value.downcast_ref::<NaiveDate>() {
144 return Ok(date.to_string());
145 }
146
147 if let Some(timestamp) = value.downcast_ref::<DateTime<Tz>>() {
148 return Ok(timestamp.to_string());
149 }
150
151 if let Some(price_proto) = value.downcast_ref::<PriceProto>() {
152 if let Some(ref uuid_proto) = price_proto.uuid {
153 let uuid = ProtoSerializationUtil::deserialize_uuid(uuid_proto)
154 .map_err(|_| "Failed to convert UuidProto to Uuid")?;
155 return Ok(uuid.to_string());
156 }
157 }
158
159 Ok("Unknown field type".to_string())
161 }
162
163 pub fn get_measures(&self) -> Vec<MeasureMapEntry> {
164 self.position_proto.measures.clone()
165 }
166
167 pub fn get_fields(&self) -> Vec<FieldMapEntry> {
168 self.position_proto.fields.clone()
169 }
170
171 pub fn to_string(&self) -> String {
172 let mut output = String::new();
173
174 for field in self.get_fields() {
175 let field_name = FieldProto::from_i32(field.field)
176 .map(|f| f.as_str_name())
177 .unwrap_or("UNKNOWN");
178 output.push_str(field_name);
179 output.push(',');
180
181 if let Ok(display) = self.get_field_display(field) {
182 output.push_str(&display);
183 }
184 output.push(';');
185 }
186
187 for measure in self.get_measures() {
188 let measure_name = match measure.measure {
191 0 => "UNKNOWN_MEASURE",
192 1 => "DIRECTED_QUANTITY",
193 2 => "MARKET_VALUE",
194 3 => "UNADJUSTED_COST_BASIS",
195 4 => "ADJUSTED_COST_BASIS",
196 5 => "CURRENT_YIELD",
197 6 => "YIELD_TO_MATURITY",
198 _ => "UNKNOWN",
199 };
200 output.push_str(measure_name);
201 output.push(',');
202
203 if let Ok(decimal) = self.get_measure(measure) {
204 output.push_str(&decimal.to_string());
205 }
206 output.push(';');
207 }
208
209 output
210 }
211
212 fn wrap_string_to_any(my_string: &str) -> prost_types::Any {
213 Any {
216 type_url: "type.googleapis.com/google.protobuf.StringValue".to_string(),
217 value: my_string.as_bytes().to_vec(),
218 }
219 }
220
221 fn pack_field(field_to_pack: &dyn std::any::Any) -> Result<prost_types::Any, &'static str> {
222 if let Some(date_proto) = field_to_pack.downcast_ref::<LocalDateProto>() {
224 let mut bytes = Vec::new();
225 date_proto
226 .encode(&mut bytes)
227 .map_err(|_| "Failed to encode LocalDateProto")?;
228 return Ok(Any {
229 type_url: "type.googleapis.com/fintekkers.models.util.LocalDateProto".to_string(),
230 value: bytes,
231 });
232 }
233
234 Err("Unsupported field type for packing")
235 }
236
237 fn unpack_field(
238 field_to_unpack: &FieldMapEntry,
239 ) -> Result<Box<dyn std::any::Any>, &'static str> {
240 let packed_value = match &field_to_unpack.field_map_value_one_of {
241 Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(any)) => any,
242 _ => return Err("Field value is required"),
243 };
244
245 let binary_value = &packed_value.value;
246 let field_enum =
247 FieldProto::from_i32(field_to_unpack.field).ok_or("Invalid field enum value")?;
248
249 match field_enum {
250 FieldProto::PortfolioId
251 | FieldProto::SecurityId
252 | FieldProto::PriceId
253 | FieldProto::Id => {
254 let uuid_proto = UuidProto::decode(binary_value.as_slice())
255 .map_err(|_| "Failed to decode UUIDProto")?;
256 let uuid = ProtoSerializationUtil::deserialize_uuid(&uuid_proto)
257 .map_err(|_| "Failed to convert UuidProto to Uuid")?;
258 Ok(Box::new(uuid))
259 }
260 FieldProto::AsOf => {
261 let timestamp_proto = LocalTimestampProto::decode(binary_value.as_slice())
262 .map_err(|_| "Failed to decode LocalTimestampProto")?;
263 let timestamp = ProtoSerializationUtil::deserialize_timestamp(×tamp_proto)
264 .map_err(|_| "Failed to convert LocalTimestampProto to DateTime")?;
265 Ok(Box::new(timestamp))
266 }
267 FieldProto::TradeDate
268 | FieldProto::MaturityDate
269 | FieldProto::IssueDate
270 | FieldProto::SettlementDate
271 | FieldProto::TaxLotOpenDate
272 | FieldProto::TaxLotCloseDate
273 | FieldProto::EffectiveDate => {
274 let date_proto = LocalDateProto::decode(binary_value.as_slice())
275 .map_err(|_| "Failed to decode LocalDateProto")?;
276 let date = ProtoSerializationUtil::deserialize_date(&date_proto)
277 .map_err(|_| "Failed to convert LocalDateProto to NaiveDate")?;
278 Ok(Box::new(date))
279 }
280 FieldProto::Identifier => {
281 let identifier_proto = IdentifierProto::decode(binary_value.as_slice())
282 .map_err(|_| "Failed to decode IdentifierProto")?;
283 Ok(Box::new(identifier_proto))
284 }
285 FieldProto::Strategy => {
286 let strategy_proto = StrategyProto::decode(binary_value.as_slice())
287 .map_err(|_| "Failed to decode StrategyProto")?;
288 Ok(Box::new(strategy_proto))
289 }
290 FieldProto::Tenor | FieldProto::AdjustedTenor => {
291 let tenor_proto = TenorProto::decode(binary_value.as_slice())
292 .map_err(|_| "Failed to decode TenorProto")?;
293 Ok(Box::new(tenor_proto))
294 }
295 FieldProto::Price => {
296 let price_proto = PriceProto::decode(binary_value.as_slice())
297 .map_err(|_| "Failed to decode PriceProto")?;
298 Ok(Box::new(price_proto))
299 }
300 FieldProto::PortfolioName
301 | FieldProto::SecurityDescription
302 | FieldProto::SecurityIssuerName
303 | FieldProto::ProductType
304 | FieldProto::ProductClass
305 | FieldProto::AssetClass => {
306 Err("StringValue decoding not yet implemented - needs prost_types StringValue support")
312 }
313 FieldProto::Portfolio => {
314 let portfolio_proto = PortfolioProto::decode(binary_value.as_slice())
315 .map_err(|_| "Failed to decode PortfolioProto")?;
316 Ok(Box::new(portfolio_proto))
317 }
318 FieldProto::Security | FieldProto::CashImpactSecurity => {
319 let security_proto = SecurityProto::decode(binary_value.as_slice())
320 .map_err(|_| "Failed to decode SecurityProto")?;
321 Ok(Box::new(security_proto))
322 }
323 FieldProto::TransactionType | FieldProto::PositionStatus => {
324 if let Some(field_map_entry::FieldMapValueOneOf::EnumValue(val)) =
326 &field_to_unpack.field_map_value_one_of
327 {
328 Ok(Box::new(*val))
329 } else {
330 Err("Enum value expected for TransactionType or PositionStatus")
331 }
332 }
333 _ => Err("Field not found. Could not unpack field. Mapping missing"),
334 }
335 }
336
337 fn unpack_measure(measure_to_unpack: &MeasureMapEntry) -> Result<Decimal, &'static str> {
338 let decimal_proto = measure_to_unpack
339 .measure_decimal_value
340 .as_ref()
341 .ok_or("Measure decimal value is required")?;
342 ProtoSerializationUtil::deserialize_decimal(decimal_proto)
343 .map_err(|_| "Failed to convert DecimalValueProto to Decimal")
344 }
345}
346
347#[derive(Debug)]
348pub struct PositionFilter {
349 filter_proto: PositionFilterProto,
350}
351
352impl PositionFilter {
353 pub fn new(filter_proto: PositionFilterProto) -> PositionFilter {
354 PositionFilter { filter_proto }
355 }
356
357 pub fn get_filters(&self) -> &[FieldMapEntry] {
358 &self.filter_proto.filters
359 }
360
361 pub fn to_proto(&self) -> PositionFilterProto {
362 self.filter_proto.clone()
363 }
364
365 pub fn find_filter(&self, field: FieldProto) -> Option<&FieldMapEntry> {
367 self.filter_proto
368 .filters
369 .iter()
370 .find(|f| f.field == field as i32)
371 }
372
373 pub fn get_uuid(&self, field: FieldProto) -> Result<Uuid, &'static str> {
379 let entry = self
380 .find_filter(field)
381 .ok_or("Field not found in filter")?;
382
383 let packed_value = match &entry.field_map_value_one_of {
384 Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(any)) => any,
385 _ => return Err("Expected packed value for UUID field"),
386 };
387
388 let uuid_proto = UuidProto::decode(packed_value.value.as_slice())
389 .map_err(|_| "Failed to decode UUIDProto")?;
390 ProtoSerializationUtil::deserialize_uuid(&uuid_proto)
391 .map_err(|_| "Failed to convert UuidProto to Uuid")
392 }
393
394 pub fn get_string(&self, field: FieldProto) -> Result<String, &'static str> {
397 let entry = self
398 .find_filter(field)
399 .ok_or("Field not found in filter")?;
400
401 match &entry.field_map_value_one_of {
402 Some(field_map_entry::FieldMapValueOneOf::StringValue(s)) => Ok(s.clone()),
403 _ => Err("Expected string value for this field"),
404 }
405 }
406
407 pub fn get_date(&self, field: FieldProto) -> Result<NaiveDate, &'static str> {
410 let entry = self
411 .find_filter(field)
412 .ok_or("Field not found in filter")?;
413
414 let packed_value = match &entry.field_map_value_one_of {
415 Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(any)) => any,
416 _ => return Err("Expected packed value for date field"),
417 };
418
419 let date_proto = LocalDateProto::decode(packed_value.value.as_slice())
420 .map_err(|_| "Failed to decode LocalDateProto")?;
421 ProtoSerializationUtil::deserialize_date(&date_proto)
422 .map_err(|_| "Failed to convert LocalDateProto to NaiveDate")
423 }
424
425 pub fn get_enum_value(&self, field: FieldProto) -> Result<i32, &'static str> {
428 let entry = self
429 .find_filter(field)
430 .ok_or("Field not found in filter")?;
431
432 match &entry.field_map_value_one_of {
433 Some(field_map_entry::FieldMapValueOneOf::EnumValue(val)) => Ok(*val),
434 _ => Err("Expected enum value for this field"),
435 }
436 }
437
438 pub fn get_timestamp(&self, field: FieldProto) -> Result<DateTime<Tz>, &'static str> {
440 let entry = self
441 .find_filter(field)
442 .ok_or("Field not found in filter")?;
443
444 let packed_value = match &entry.field_map_value_one_of {
445 Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(any)) => any,
446 _ => return Err("Expected packed value for timestamp field"),
447 };
448
449 let timestamp_proto = LocalTimestampProto::decode(packed_value.value.as_slice())
450 .map_err(|_| "Failed to decode LocalTimestampProto")?;
451 ProtoSerializationUtil::deserialize_timestamp(×tamp_proto)
452 .map_err(|_| "Failed to convert LocalTimestampProto to DateTime")
453 }
454}
455
456impl TryFrom<&[u8]> for PositionFilterProto {
457 type Error = Error;
458
459 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
460 PositionFilterProto::decode(bytes).map_err(|_| Error::DeserializationError)
461 }
462}
463
464impl TryFrom<Vec<u8>> for PositionFilterProto {
465 type Error = Error;
466
467 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
468 PositionFilterProto::decode(bytes.as_slice()).map_err(|_| Error::DeserializationError)
469 }
470}
471
472#[cfg(test)]
473mod test {
474 use super::*;
475 use crate::fintekkers::models::position::{field_map_entry, PositionFilterOperator};
476 use crate::fintekkers::models::util::DecimalValueProto;
477 use crate::fintekkers::models::util::UuidProto;
478 use crate::fintekkers::wrappers::models::utils::uuid_wrapper::UUIDWrapper;
479 use chrono::naive::NaiveDate;
480 use chrono::{DateTime, Datelike};
481 use chrono_tz::Tz;
482 use prost_types::Any;
483 use uuid::Uuid;
484
485 fn create_uuid_field_entry(field: FieldProto, uuid_str: &str) -> FieldMapEntry {
487 let uuid = Uuid::parse_str(uuid_str).unwrap();
488 let uuid_proto = UuidProto {
489 raw_uuid: uuid.as_bytes().to_vec(),
490 };
491 let mut uuid_bytes = Vec::new();
492 uuid_proto.encode(&mut uuid_bytes).unwrap();
493
494 let packed_uuid = Any {
495 type_url: "type.googleapis.com/fintekkers.models.util.UUIDProto".to_string(),
496 value: uuid_bytes,
497 };
498
499 FieldMapEntry {
500 field: field as i32,
501 operator: 0,
502 field_map_value_one_of: Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(
503 packed_uuid,
504 )),
505 }
506 }
507
508 fn create_date_field_entry(
510 field: FieldProto,
511 year: u32,
512 month: u32,
513 day: u32,
514 ) -> FieldMapEntry {
515 let date_proto = LocalDateProto { year, month, day };
516 let mut date_bytes = Vec::new();
517 date_proto.encode(&mut date_bytes).unwrap();
518
519 let packed_date = Any {
520 type_url: "type.googleapis.com/fintekkers.models.util.LocalDateProto".to_string(),
521 value: date_bytes,
522 };
523
524 FieldMapEntry {
525 field: field as i32,
526 operator: 0,
527 field_map_value_one_of: Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(
528 packed_date,
529 )),
530 }
531 }
532
533 fn create_timestamp_field_entry() -> FieldMapEntry {
535 let timestamp = prost_types::Timestamp {
536 seconds: 1609459200, nanos: 0,
538 };
539 let timestamp_proto = LocalTimestampProto {
540 timestamp: Some(timestamp),
541 time_zone: "America/New_York".to_string(), };
543 let mut timestamp_bytes = Vec::new();
544 timestamp_proto.encode(&mut timestamp_bytes).unwrap();
545
546 let packed_timestamp = Any {
547 type_url: "type.googleapis.com/fintekkers.models.util.LocalTimestampProto".to_string(),
548 value: timestamp_bytes,
549 };
550
551 FieldMapEntry {
552 field: FieldProto::AsOf as i32,
553 operator: 0,
554 field_map_value_one_of: Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(
555 packed_timestamp,
556 )),
557 }
558 }
559
560 fn create_string_field_entry(field: FieldProto, value: &str) -> FieldMapEntry {
562 FieldMapEntry {
563 field: field as i32,
564 operator: 0,
565 field_map_value_one_of: Some(field_map_entry::FieldMapValueOneOf::StringValue(
566 value.to_string(),
567 )),
568 }
569 }
570
571 fn create_enum_field_entry(field: FieldProto, enum_value: i32) -> FieldMapEntry {
573 FieldMapEntry {
574 field: field as i32,
575 operator: 0,
576 field_map_value_one_of: Some(field_map_entry::FieldMapValueOneOf::EnumValue(
577 enum_value,
578 )),
579 }
580 }
581
582 fn create_measure_entry(measure: MeasureProto, value: &str) -> MeasureMapEntry {
584 MeasureMapEntry {
585 measure: measure as i32,
586 measure_decimal_value: Some(DecimalValueProto {
587 arbitrary_precision_value: value.to_string(),
588 }),
589 }
590 }
591
592 #[test]
593 fn test_position_filter_with_security_id_uuid() {
594 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
596 let uuid_wrapper = UUIDWrapper::new(UuidProto {
597 raw_uuid: uuid.as_bytes().to_vec(),
598 });
599
600 let uuid_proto: UuidProto = uuid_wrapper.into();
602 let mut uuid_bytes = Vec::new();
603 uuid_proto.encode(&mut uuid_bytes).unwrap();
604
605 let packed_uuid = Any {
607 type_url: "type.googleapis.com/fintekkers.models.util.UUIDProto".to_string(),
608 value: uuid_bytes,
609 };
610
611 let field_entry = FieldMapEntry {
613 field: FieldProto::SecurityId as i32,
614 operator: PositionFilterOperator::Equals as i32,
615 field_map_value_one_of: Some(field_map_entry::FieldMapValueOneOf::FieldValuePacked(
616 packed_uuid,
617 )),
618 };
619
620 let filter_proto = PositionFilterProto {
622 object_class: "PositionFilter".to_string(),
623 version: "0.0.1".to_string(),
624 filters: vec![field_entry],
625 };
626
627 let position_filter = PositionFilter::new(filter_proto.clone());
629
630 let filters = position_filter.get_filters();
632 assert_eq!(filters.len(), 1);
633 assert_eq!(filters[0].field, FieldProto::SecurityId as i32);
634 assert_eq!(filters[0].operator, PositionFilterOperator::Equals as i32);
635
636 let retrieved_proto = position_filter.to_proto();
638 assert_eq!(retrieved_proto.object_class, "PositionFilter");
639 assert_eq!(retrieved_proto.version, "0.0.1");
640 assert_eq!(retrieved_proto.filters.len(), 1);
641 }
642
643 #[test]
644 fn test_position_with_string_field() {
645 let field_entry = create_string_field_entry(FieldProto::PortfolioName, "Test Portfolio");
646 let position_proto = PositionProto {
647 object_class: "Position".to_string(),
648 version: "0.0.1".to_string(),
649 position_view: 0,
650 position_type: 0,
651 measures: vec![],
652 reporting_currency: None,
653 fields: vec![field_entry.clone()],
654 };
655
656 let position = Position::new(position_proto);
657
658 let value = position.get_field_value(FieldProto::PortfolioName).unwrap();
660 let string_value = value.downcast_ref::<String>().unwrap();
661 assert_eq!(string_value, "Test Portfolio");
662
663 let field_value = position.get_field(field_entry.clone()).unwrap();
665 let string_value = field_value.downcast_ref::<String>().unwrap();
666 assert_eq!(string_value, "Test Portfolio");
667
668 let display = position.get_field_display(field_entry).unwrap();
670 assert_eq!(display, "Test Portfolio");
671 }
672
673 #[test]
674 fn test_position_with_uuid_fields() {
675 let id_entry =
676 create_uuid_field_entry(FieldProto::Id, "550e8400-e29b-41d4-a716-446655440000");
677 let security_id_entry = create_uuid_field_entry(
678 FieldProto::SecurityId,
679 "660e8400-e29b-41d4-a716-446655440001",
680 );
681 let portfolio_id_entry = create_uuid_field_entry(
682 FieldProto::PortfolioId,
683 "770e8400-e29b-41d4-a716-446655440002",
684 );
685
686 let position_proto = PositionProto {
687 object_class: "Position".to_string(),
688 version: "0.0.1".to_string(),
689 position_view: 0,
690 position_type: 0,
691 measures: vec![],
692 reporting_currency: None,
693 fields: vec![
694 id_entry.clone(),
695 security_id_entry.clone(),
696 portfolio_id_entry.clone(),
697 ],
698 };
699
700 let position = Position::new(position_proto);
701
702 let value = position.get_field_value(FieldProto::Id).unwrap();
704 let uuid = value.downcast_ref::<Uuid>().unwrap();
705 assert_eq!(uuid.to_string(), "550e8400-e29b-41d4-a716-446655440000");
706
707 let value = position.get_field_value(FieldProto::SecurityId).unwrap();
709 let uuid = value.downcast_ref::<Uuid>().unwrap();
710 assert_eq!(uuid.to_string(), "660e8400-e29b-41d4-a716-446655440001");
711
712 let value = position.get_field_value(FieldProto::PortfolioId).unwrap();
714 let uuid = value.downcast_ref::<Uuid>().unwrap();
715 assert_eq!(uuid.to_string(), "770e8400-e29b-41d4-a716-446655440002");
716 }
717
718 #[test]
719 fn test_position_with_date_fields() {
720 let trade_date_entry = create_date_field_entry(FieldProto::TradeDate, 2023, 10, 15);
721 let maturity_date_entry = create_date_field_entry(FieldProto::MaturityDate, 2025, 12, 31);
722 let settlement_date_entry =
723 create_date_field_entry(FieldProto::SettlementDate, 2023, 10, 17);
724
725 let position_proto = PositionProto {
726 object_class: "Position".to_string(),
727 version: "0.0.1".to_string(),
728 position_view: 0,
729 position_type: 0,
730 measures: vec![],
731 reporting_currency: None,
732 fields: vec![
733 trade_date_entry.clone(),
734 maturity_date_entry.clone(),
735 settlement_date_entry.clone(),
736 ],
737 };
738
739 let position = Position::new(position_proto);
740
741 let value = position.get_field_value(FieldProto::TradeDate).unwrap();
743 let date = value.downcast_ref::<NaiveDate>().unwrap();
744 assert_eq!(date.year(), 2023);
745 assert_eq!(date.month(), 10);
746 assert_eq!(date.day(), 15);
747
748 let display = position.get_field_display(trade_date_entry).unwrap();
750 assert_eq!(display, "2023-10-15");
751
752 let value = position.get_field_value(FieldProto::MaturityDate).unwrap();
754 let date = value.downcast_ref::<NaiveDate>().unwrap();
755 assert_eq!(date.year(), 2025);
756 assert_eq!(date.month(), 12);
757 assert_eq!(date.day(), 31);
758 }
759
760 #[test]
761 fn test_position_with_timestamp_field() {
762 let as_of_entry = create_timestamp_field_entry();
763
764 let position_proto = PositionProto {
765 object_class: "Position".to_string(),
766 version: "0.0.1".to_string(),
767 position_view: 0,
768 position_type: 0,
769 measures: vec![],
770 reporting_currency: None,
771 fields: vec![as_of_entry.clone()],
772 };
773
774 let position = Position::new(position_proto);
775
776 let value = position.get_field_value(FieldProto::AsOf).unwrap();
778 let timestamp = value.downcast_ref::<DateTime<Tz>>().unwrap();
779
780 assert_eq!(timestamp.year(), 2020);
783 assert_eq!(timestamp.month(), 12);
784 assert_eq!(timestamp.day(), 31);
785
786 let timezone_str = timestamp.timezone().to_string();
788 assert!(
789 timezone_str.contains("EST")
790 || timezone_str.contains("EDT")
791 || timezone_str.contains("America/New_York")
792 );
793
794 let display = position.get_field_display(as_of_entry).unwrap();
796 assert!(display.contains("2020")); }
798
799 #[test]
800 fn test_position_with_enum_field() {
801 let transaction_type_entry = create_enum_field_entry(FieldProto::TransactionType, 1);
803
804 let position_proto = PositionProto {
805 object_class: "Position".to_string(),
806 version: "0.0.1".to_string(),
807 position_view: 0,
808 position_type: 0,
809 measures: vec![],
810 reporting_currency: None,
811 fields: vec![transaction_type_entry.clone()],
812 };
813
814 let position = Position::new(position_proto);
815
816 let value = position
818 .get_field_value(FieldProto::TransactionType)
819 .unwrap();
820 let enum_value = value.downcast_ref::<i32>().unwrap();
821 assert_eq!(*enum_value, 1);
822
823 let display = position.get_field_display(transaction_type_entry).unwrap();
825 assert_eq!(display, "1");
826 }
827
828 #[test]
829 fn test_position_with_decimal_measures() {
830 let directed_quantity = create_measure_entry(MeasureProto::DirectedQuantity, "100.50");
831 let market_value = create_measure_entry(MeasureProto::MarketValue, "50000.75");
832
833 let position_proto = PositionProto {
834 object_class: "Position".to_string(),
835 version: "0.0.1".to_string(),
836 position_view: 0,
837 position_type: 0,
838 measures: vec![directed_quantity.clone(), market_value.clone()],
839 reporting_currency: None,
840 fields: vec![],
841 };
842
843 let position = Position::new(position_proto);
844
845 let measure = position
847 .get_measure_value(MeasureProto::DirectedQuantity)
848 .unwrap();
849 assert_eq!(measure.to_string(), "100.50");
850
851 let measure = position.get_measure(directed_quantity).unwrap();
853 assert_eq!(measure.to_string(), "100.50");
854
855 let measure = position
857 .get_measure_value(MeasureProto::MarketValue)
858 .unwrap();
859 assert_eq!(measure.to_string(), "50000.75");
860 }
861
862 #[test]
863 fn test_position_get_fields_and_measures() {
864 let field1 = create_string_field_entry(FieldProto::PortfolioName, "Portfolio 1");
865 let field2 =
866 create_uuid_field_entry(FieldProto::Id, "550e8400-e29b-41d4-a716-446655440000");
867 let measure1 = create_measure_entry(MeasureProto::DirectedQuantity, "100.0");
868
869 let position_proto = PositionProto {
870 object_class: "Position".to_string(),
871 version: "0.0.1".to_string(),
872 position_view: 0,
873 position_type: 0,
874 measures: vec![measure1],
875 reporting_currency: None,
876 fields: vec![field1, field2],
877 };
878
879 let position = Position::new(position_proto);
880
881 let fields = position.get_fields();
883 assert_eq!(fields.len(), 2);
884
885 let measures = position.get_measures();
887 assert_eq!(measures.len(), 1);
888 }
889
890 #[test]
891 fn test_position_to_string() {
892 let field1 = create_string_field_entry(FieldProto::PortfolioName, "Test Portfolio");
893 let field2 = create_date_field_entry(FieldProto::TradeDate, 2023, 10, 15);
894 let measure1 = create_measure_entry(MeasureProto::DirectedQuantity, "100.50");
895
896 let position_proto = PositionProto {
897 object_class: "Position".to_string(),
898 version: "0.0.1".to_string(),
899 position_view: 0,
900 position_type: 0,
901 measures: vec![measure1],
902 reporting_currency: None,
903 fields: vec![field1, field2],
904 };
905
906 let position = Position::new(position_proto);
907
908 let result = position.to_string();
909 assert!(result.contains("PORTFOLIO_NAME"));
910 assert!(result.contains("Test Portfolio"));
911 assert!(result.contains("TRADE_DATE"));
912 assert!(result.contains("2023-10-15"));
913 assert!(result.contains("DIRECTED_QUANTITY"));
914 assert!(result.contains("100.50"));
915 }
916
917 #[test]
918 fn test_position_field_not_found() {
919 let position_proto = PositionProto {
920 object_class: "Position".to_string(),
921 version: "0.0.1".to_string(),
922 position_view: 0,
923 position_type: 0,
924 measures: vec![],
925 reporting_currency: None,
926 fields: vec![],
927 };
928
929 let position = Position::new(position_proto);
930
931 let result = position.get_field_value(FieldProto::PortfolioName);
933 assert!(result.is_err());
934 assert_eq!(result.unwrap_err(), "Could not find field in position");
935
936 let result = position.get_measure_value(MeasureProto::DirectedQuantity);
938 assert!(result.is_err());
939 assert_eq!(result.unwrap_err(), "Could not find measure in position");
940 }
941
942 #[test]
943 fn test_position_filter_get_uuid() {
944 let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
945 let field_entry = create_uuid_field_entry(FieldProto::SecurityId, uuid_str);
946 let filter = PositionFilter::new(PositionFilterProto {
947 object_class: "PositionFilter".to_string(),
948 version: "0.0.1".to_string(),
949 filters: vec![field_entry],
950 });
951
952 let uuid = filter.get_uuid(FieldProto::SecurityId).unwrap();
953 assert_eq!(uuid.to_string(), uuid_str);
954 }
955
956 #[test]
957 fn test_position_filter_get_uuid_not_found() {
958 let filter = PositionFilter::new(PositionFilterProto {
959 object_class: "PositionFilter".to_string(),
960 version: "0.0.1".to_string(),
961 filters: vec![],
962 });
963
964 let result = filter.get_uuid(FieldProto::SecurityId);
965 assert!(result.is_err());
966 assert_eq!(result.unwrap_err(), "Field not found in filter");
967 }
968
969 #[test]
970 fn test_position_filter_get_string() {
971 let field_entry = create_string_field_entry(FieldProto::AssetClass, "Fixed Income");
972 let filter = PositionFilter::new(PositionFilterProto {
973 object_class: "PositionFilter".to_string(),
974 version: "0.0.1".to_string(),
975 filters: vec![field_entry],
976 });
977
978 let value = filter.get_string(FieldProto::AssetClass).unwrap();
979 assert_eq!(value, "Fixed Income");
980 }
981
982 #[test]
983 fn test_position_filter_get_date() {
984 let field_entry = create_date_field_entry(FieldProto::MaturityDate, 2030, 6, 15);
985 let filter = PositionFilter::new(PositionFilterProto {
986 object_class: "PositionFilter".to_string(),
987 version: "0.0.1".to_string(),
988 filters: vec![field_entry],
989 });
990
991 let date = filter.get_date(FieldProto::MaturityDate).unwrap();
992 assert_eq!(date.year(), 2030);
993 assert_eq!(date.month(), 6);
994 assert_eq!(date.day(), 15);
995 }
996
997 #[test]
998 fn test_position_filter_get_enum_value() {
999 let field_entry = create_enum_field_entry(FieldProto::TransactionType, 1); let filter = PositionFilter::new(PositionFilterProto {
1001 object_class: "PositionFilter".to_string(),
1002 version: "0.0.1".to_string(),
1003 filters: vec![field_entry],
1004 });
1005
1006 let value = filter.get_enum_value(FieldProto::TransactionType).unwrap();
1007 assert_eq!(value, 1);
1008 }
1009
1010 #[test]
1011 fn test_position_filter_multiple_filters() {
1012 let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
1013 let uuid_entry = create_uuid_field_entry(FieldProto::SecurityId, uuid_str);
1014 let string_entry = create_string_field_entry(FieldProto::AssetClass, "Fixed Income");
1015 let date_entry = create_date_field_entry(FieldProto::MaturityDate, 2030, 1, 15);
1016
1017 let filter = PositionFilter::new(PositionFilterProto {
1018 object_class: "PositionFilter".to_string(),
1019 version: "0.0.1".to_string(),
1020 filters: vec![uuid_entry, string_entry, date_entry],
1021 });
1022
1023 assert_eq!(
1025 filter.get_uuid(FieldProto::SecurityId).unwrap().to_string(),
1026 uuid_str
1027 );
1028 assert_eq!(
1029 filter.get_string(FieldProto::AssetClass).unwrap(),
1030 "Fixed Income"
1031 );
1032 assert_eq!(filter.get_date(FieldProto::MaturityDate).unwrap().year(), 2030);
1033 }
1034
1035 #[test]
1036 fn test_position_with_multiple_field_types() {
1037 let string_field = create_string_field_entry(FieldProto::AssetClass, "Equity");
1039 let uuid_field = create_uuid_field_entry(
1040 FieldProto::SecurityId,
1041 "880e8400-e29b-41d4-a716-446655440003",
1042 );
1043 let date_field = create_date_field_entry(FieldProto::MaturityDate, 2024, 6, 30);
1044 let enum_field = create_enum_field_entry(FieldProto::PositionStatus, 1);
1045 let timestamp_field = create_timestamp_field_entry();
1046
1047 let position_proto = PositionProto {
1048 object_class: "Position".to_string(),
1049 version: "0.0.1".to_string(),
1050 position_view: 0,
1051 position_type: 0,
1052 measures: vec![
1053 create_measure_entry(MeasureProto::DirectedQuantity, "50.25"),
1054 create_measure_entry(MeasureProto::MarketValue, "25000.00"),
1055 ],
1056 reporting_currency: None,
1057 fields: vec![
1058 string_field.clone(),
1059 uuid_field.clone(),
1060 date_field.clone(),
1061 enum_field.clone(),
1062 timestamp_field.clone(),
1063 ],
1064 };
1065
1066 let position = Position::new(position_proto);
1067
1068 assert!(position.get_field_value(FieldProto::AssetClass).is_ok());
1070 assert!(position.get_field_value(FieldProto::SecurityId).is_ok());
1071 assert!(position.get_field_value(FieldProto::MaturityDate).is_ok());
1072 assert!(position.get_field_value(FieldProto::PositionStatus).is_ok());
1073 assert!(position.get_field_value(FieldProto::AsOf).is_ok());
1074
1075 assert!(position
1077 .get_measure_value(MeasureProto::DirectedQuantity)
1078 .is_ok());
1079 assert!(position
1080 .get_measure_value(MeasureProto::MarketValue)
1081 .is_ok());
1082
1083 assert!(position.get_field_display(string_field).is_ok());
1085 assert!(position.get_field_display(uuid_field).is_ok());
1086 assert!(position.get_field_display(date_field).is_ok());
1087 assert!(position.get_field_display(enum_field).is_ok());
1088 assert!(position.get_field_display(timestamp_field).is_ok());
1089 }
1090}
1091
1092