stock_script/
lib.rs

1use std::str::FromStr;
2
3/// Represents a statement in StockScript
4#[derive(Debug, PartialEq)]
5pub struct Statement {
6    pub symbol: String,
7    pub weight: f64,
8}
9
10impl FromStr for Statement {
11    type Err = &'static str;
12
13    fn from_str(s: &str) -> Result<Self, Self::Err> {
14        let parts: Vec<&str> = s.split("::").map(str::trim).collect();
15        if parts.len() != 2 {
16            return Err("Invalid format: Expected 'SYMBOL::WEIGHT'");
17        }
18
19        let symbol = parts[0].to_string();
20        let weight: f64 = parts[1].parse().map_err(|_| "Invalid weight")?;
21
22        Ok(Statement { symbol, weight })
23    }
24}
25
26/// Parses a StockScript string into a vector of Statements
27pub fn parse_statements(input: &str) -> Result<Vec<Statement>, &'static str> {
28    input
29        .split(';')
30        .filter(|s| !s.trim().is_empty()) // Ignore empty parts
31        .map(|s| s.parse())
32        .collect()
33}
34
35/// Serializes a vector of Statements into a valid StockScript string
36pub fn serialize_statements(statements: &[Statement]) -> String {
37    statements
38        .iter()
39        .map(|s| format!("{}::{}", s.symbol.trim(), s.weight))
40        .collect::<Vec<_>>()
41        .join(";")
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn test_parse_statements() {
50        let input = "  AAPL  ::  0.25 ; TSLA::0.15  ;  GOOGL::0.60  ";
51        let expected = vec![
52            Statement {
53                symbol: "AAPL".to_string(),
54                weight: 0.25,
55            },
56            Statement {
57                symbol: "TSLA".to_string(),
58                weight: 0.15,
59            },
60            Statement {
61                symbol: "GOOGL".to_string(),
62                weight: 0.60,
63            },
64        ];
65
66        let result = parse_statements(input);
67        assert_eq!(result, Ok(expected));
68    }
69
70    #[test]
71    fn test_serialize_statements() {
72        let statements = vec![
73            Statement {
74                symbol: "AAPL".to_string(),
75                weight: 0.25,
76            },
77            Statement {
78                symbol: "TSLA".to_string(),
79                weight: 0.15,
80            },
81            Statement {
82                symbol: "GOOGL".to_string(),
83                weight: 0.60,
84            },
85        ];
86
87        let expected = "AAPL::0.25;TSLA::0.15;GOOGL::0.6";
88        let result = serialize_statements(&statements);
89        assert_eq!(result, expected);
90    }
91
92    #[test]
93    fn test_parse_invalid_format() {
94        let input = "INVALID";
95        let result = parse_statements(input);
96        assert!(result.is_err());
97    }
98
99    #[test]
100    fn test_parse_empty_input() {
101        let input = "";
102        let result = parse_statements(input);
103        assert_eq!(result, Ok(vec![]));
104    }
105
106    #[test]
107    fn test_parse_extra_whitespace() {
108        let input = "  MSFT  ::  1.0  ;  AMZN::0.5  ";
109        let expected = vec![
110            Statement {
111                symbol: "MSFT".to_string(),
112                weight: 1.0,
113            },
114            Statement {
115                symbol: "AMZN".to_string(),
116                weight: 0.5,
117            },
118        ];
119        let result = parse_statements(input);
120        assert_eq!(result, Ok(expected));
121    }
122
123    #[test]
124    fn test_serialize_empty_list() {
125        let statements = vec![];
126        let result = serialize_statements(&statements);
127        assert_eq!(result, "");
128    }
129}