1use std::sync::Arc;
2
3use anyhow::Result;
4use bson::{doc, oid::ObjectId};
5use futures::stream::TryStreamExt;
6use mongodb::{ClientSession, Database};
7use serde::{Deserialize, Serialize};
8use tokio::sync::Mutex;
9
10use crate::db_util;
11
12#[derive(Serialize, Deserialize, Debug, PartialEq)]
13pub enum SecurityType {
14 Stock,
15}
16#[derive(Serialize, Deserialize, Debug, PartialEq)]
17pub struct Security {
18 _id: bson::oid::ObjectId,
19 listing_exchange: String,
20 security_type: SecurityType,
21 ticker: String,
22 ibkr_conid: Option<u32>,
23}
24
25impl Security {
26 pub const COLLECTION_NAME: &'static str = "securities";
27
28 pub fn new(
29 security_type: SecurityType,
30 ticker: &str,
31 listing_exchange: &str,
32 ibkr_conid: Option<u32>,
33 ) -> Self {
34 Self {
35 _id: ObjectId::new(),
36 listing_exchange: listing_exchange.to_owned(),
37 security_type,
38 ticker: ticker.to_owned(),
39 ibkr_conid,
40 }
41 }
42
43 pub fn id(&self) -> ObjectId {
44 self._id
45 }
46
47 pub fn listing_exchange(&self) -> &str {
48 &self.listing_exchange
49 }
50
51 pub fn security_type(&self) -> &SecurityType {
52 &self.security_type
53 }
54
55 pub fn ticker(&self) -> &str {
56 &self.ticker
57 }
58
59 pub fn ibkr_conid(&self) -> Option<u32> {
60 self.ibkr_conid
61 }
62
63 pub async fn insert(
64 &self,
65 db: &Database,
66 session: Option<Arc<Mutex<ClientSession>>>,
67 ) -> Result<()> {
68 db_util::insert(self, db, Self::COLLECTION_NAME, session).await
69 }
70
71 pub async fn find_by_id(db: &Database, id: ObjectId) -> Result<Option<Self>> {
72 let result = db
73 .collection::<Self>(Self::COLLECTION_NAME)
74 .find_one(bson::doc! {"_id": id})
75 .await?;
76
77 Ok(result)
78 }
79
80 pub async fn find_by_ticker_and_exchange(
81 db: &Database,
82 ticker: &str,
83 listing_exchange: &str,
84 ) -> Result<Option<Self>> {
85 let result = db
86 .collection::<Self>(Self::COLLECTION_NAME)
87 .find_one(bson::doc! {"ticker": ticker, "listing_exchange": listing_exchange})
88 .await?;
89
90 Ok(result)
91 }
92
93 pub async fn find_by_conid(db: &Database, ibkr_conid: u32) -> Result<Option<Self>> {
94 Ok(db
95 .collection::<Self>(Self::COLLECTION_NAME)
96 .find_one(doc! { "ibkr_conid": ibkr_conid })
97 .await?)
98 }
99
100 pub async fn find_by_ticker(db: &Database, ticker: &str) -> Result<Vec<Self>> {
101 let result = db
102 .collection::<Self>(Self::COLLECTION_NAME)
103 .find(doc! { "ticker": ticker })
104 .await?;
105
106 Ok(result.try_collect().await?)
108 }
109}