use serde::{Deserialize, Serialize};
use serde_json::Value;
pub(crate) const DEFAULT_SYMBOL_DIALECT_TYPE: &str = "CHARTING";
pub(crate) const DEFAULT_STRING_KEYS: &[&str] =
&["value", "formattedValue", "displayValue", "name"];
pub(crate) const SCREEN_STRING_KEYS: &[&str] = &[
"formattedValue",
"value",
"displayValue",
"letterValue",
"name",
];
pub(crate) fn symbols_to_owned(symbols: &[&str]) -> Vec<String> {
symbols.iter().map(|s| (*s).to_string()).collect()
}
pub(crate) fn deserialize_first_array_element<T, E>(values: Vec<Value>) -> Result<Option<T>, E>
where
T: serde::de::DeserializeOwned,
E: serde::de::Error,
{
values
.into_iter()
.next()
.map(serde_json::from_value)
.transpose()
.map_err(E::custom)
}
pub(crate) fn deserialize_optional_string<'de, D>(
deserializer: D,
) -> Result<Option<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Option::<Value>::deserialize(deserializer)?;
Ok(value.and_then(|value| json_value_to_string(value, DEFAULT_STRING_KEYS)))
}
pub(crate) fn json_value_to_string(value: Value, object_keys: &[&str]) -> Option<String> {
match value {
Value::Null => None,
Value::String(value) => Some(value),
Value::Number(value) => Some(value.to_string()),
Value::Bool(value) => Some(value.to_string()),
Value::Array(mut values) => match values.len() {
0 => None,
1 => values
.pop()
.and_then(|value| json_value_to_string(value, object_keys)),
_ => Some(Value::Array(values).to_string()),
},
Value::Object(map) => {
for key in object_keys {
if let Some(value) = map
.get(*key)
.cloned()
.and_then(|value| json_value_to_string(value, object_keys))
{
return Some(value);
}
}
Some(Value::Object(map).to_string())
}
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SymbolVariables {
pub symbols: Vec<String>,
pub symbol_dialect_type: String,
}
impl SymbolVariables {
pub fn new(symbols: &[&str], dialect: Option<&str>) -> Self {
Self {
symbols: symbols_to_owned(symbols),
symbol_dialect_type: dialect.unwrap_or(DEFAULT_SYMBOL_DIALECT_TYPE).to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SortInformation {
pub direction: String,
pub order: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResponseColumn {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub sort_information: Option<SortInformation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FormattedFloat {
pub value: Option<f64>,
pub formatted_value: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DateValue {
pub value: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FloatValue {
pub value: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TreeNode {
pub id: Option<String>,
pub name: Option<String>,
pub parent_id: Option<String>,
#[serde(rename = "type")]
pub node_type: Option<String>,
#[serde(default)]
pub children: Vec<TreeChildNode>,
pub content_type: Option<String>,
pub tree_type: Option<String>,
pub url: Option<String>,
pub reference_id: Option<String>,
}
#[cfg(test)]
mod tests {
use super::{DEFAULT_SYMBOL_DIALECT_TYPE, SymbolVariables, symbols_to_owned};
#[test]
fn symbols_to_owned_preserves_order() {
assert_eq!(symbols_to_owned(&["AAPL", "MSFT"]), vec!["AAPL", "MSFT"]);
}
#[test]
fn symbol_variables_uses_default_dialect() {
let variables = SymbolVariables::new(&["AAPL"], None);
assert_eq!(variables.symbols, vec!["AAPL"]);
assert_eq!(variables.symbol_dialect_type, DEFAULT_SYMBOL_DIALECT_TYPE);
}
#[test]
fn symbol_variables_uses_custom_dialect() {
let variables = SymbolVariables::new(&["AAPL"], Some("CUSTOM"));
assert_eq!(variables.symbols, vec!["AAPL"]);
assert_eq!(variables.symbol_dialect_type, "CUSTOM");
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TreeChildNode {
pub id: Option<String>,
pub name: Option<String>,
#[serde(rename = "type")]
pub node_type: Option<String>,
}