Skip to content

Commit

Permalink
Paresing appdata with json format v3 (#11)
Browse files Browse the repository at this point in the history
* Parsion appdata with json format v3

* silence clippy

* clippy corrections

* nlordell's comments
  • Loading branch information
josojo authored Jun 27, 2022
1 parent b08da8d commit 4c37f08
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod app_data_json_format;
pub mod dune_json_formats;
pub mod in_memory_database;
pub mod referral_store;
pub mod u256_decimal;
68 changes: 61 additions & 7 deletions src/models/app_data_json_format.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
//! Contains the app_data file structures, as they are stored in ipfs
//!
use primitive_types::H160;
use crate::models::u256_decimal;
use primitive_types::{H160, U256};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

#[serde_as]
#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, Hash, Default)]
pub struct Referrer {
pub address: H160,
pub version: String,
}

#[serde_as]
#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, Hash, Default)]
#[serde(rename_all = "camelCase")]
pub struct Quote {
#[serde(with = "u256_decimal")]
pub sell_amount: U256,
#[serde(with = "u256_decimal")]
pub buy_amount: U256,
pub version: String,
}

#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, Hash, Default)]
pub struct Metadata {
// we make all of the field optional, in order to be compatible with all versions
pub environment: Option<String>,
pub referrer: Option<Referrer>,
pub quote: Option<Quote>,
}

#[serde_as]
#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, Hash, Default)]
#[serde(rename_all = "camelCase")]
pub struct AppData {
pub version: String,
pub app_code: String,
pub environment: Option<String>,
pub metadata: Option<Metadata>,
}

Expand All @@ -42,23 +51,63 @@ mod tests {
use serde_json::json;

#[test]
fn test_loading_json_and_reading_referral() {
fn test_loading_json_v1_and_reading_referral() {
let value = json!({
"version":"1.2.3",
"appCode":"MooSwap",
"metadata":{
"environment": "production",
"referrer":{
"kind":"referrer",
"address":"0x8c35B7eE520277D14af5F6098835A584C337311b",
"version":"6.6.6"
}
}
});
let json: AppData = serde_json::from_value(value).unwrap();
let expected = AppData {
version: "1.2.3".to_string(),
app_code: "MooSwap".to_string(),
environment: None,
metadata: Some(Metadata {
environment: Some("production".to_string()),
referrer: Some(Referrer {
address: "0x8c35B7eE520277D14af5F6098835A584C337311b"
.parse()
.unwrap(),
version: "6.6.6".to_string(),
}),
quote: None,
}),
};

assert_eq!(json, expected);
}
#[test]
fn test_loading_json_v3_and_reading_referral() {
let value = json!({
"version":"1.2.3",
"appCode":"MooSwap",
"environment": "production",
"metadata":{
"environment": "production",
"referrer":{
"kind":"referrer",
"address":"0x8c35B7eE520277D14af5F6098835A584C337311b",
"version":"6.6.6"
},
"quote": {
"version": "1.0",
"sellAmount": "23426235345",
"buyAmount": "2341253523453",
}
}
});
let json: AppData = serde_json::from_value(value).unwrap();
let expected = AppData {
version: "1.2.3".to_string(),
app_code: "MooSwap".to_string(),
environment: Some("production".to_string()),
metadata: Some(Metadata {
environment: Some("production".to_string()),
referrer: Some(Referrer {
Expand All @@ -67,6 +116,11 @@ mod tests {
.unwrap(),
version: "6.6.6".to_string(),
}),
quote: Some(Quote {
version: String::from("1.0"),
sell_amount: U256::from_dec_str("23426235345").unwrap(),
buy_amount: U256::from_dec_str("2341253523453").unwrap(),
}),
}),
};

Expand Down
7 changes: 6 additions & 1 deletion src/models/referral_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::ser::{Serialize, SerializeMap, Serializer};
use std::collections::HashMap;
use std::sync::Mutex;

#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum AppDataEntry {
Data(Option<AppData>),
Expand Down Expand Up @@ -65,6 +66,7 @@ mod tests {
let entry = AppData {
version: "1.2.3".to_string(),
app_code: "MooSwap".to_string(),
environment: None,
metadata: Some(Metadata {
environment: Some("production".to_string()),
referrer: Some(Referrer {
Expand All @@ -74,6 +76,7 @@ mod tests {
.unwrap(),
version: "6.6.6".to_string(),
}),
quote: None,
}),
};

Expand All @@ -86,12 +89,14 @@ mod tests {
"0x2947be33ebfa25686ec204857135dd1c676f35d6c252eb066fffaf9b493a01b4":{
"version":"1.2.3",
"appCode":"MooSwap",
"environment": null,
"metadata":{
"environment": "production",
"referrer":{
"address":"0x8c35b7ee520277d14af5f6098835a584c337311b",
"version":"6.6.6"
}
},
"quote": null,
}
}
});
Expand Down
78 changes: 78 additions & 0 deletions src/models/u256_decimal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use primitive_types::U256;
use serde::{de, Deserializer, Serializer};
use std::fmt;

// Code copied from here: https://github.com/cowprotocol/services/blob/main/crates/model/src/u256_decimal.rs
// It was copied, as we prefer to not depend on such a big project.

pub struct DecimalU256;

pub fn serialize<S>(value: &U256, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&value.to_string())
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor {}
impl<'de> de::Visitor<'de> for Visitor {
type Value = U256;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a u256 encoded as a decimal encoded string")
}

fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
U256::from_dec_str(s).map_err(|err| {
de::Error::custom(format!("failed to decode {:?} as decimal u256: {}", s, err))
})
}
}

deserializer.deserialize_str(Visitor {})
}

/// Converts an amount of units of an ERC20 token with the specified amount of
/// decimals into its decimal representation as a string.
///
pub fn format_units(amount: U256, decimals: usize) -> String {
let str_amount = amount.to_string();
if decimals == 0 {
str_amount
} else if str_amount.len() <= decimals {
format!("0.{:0>pad_left$}", str_amount, pad_left = decimals)
} else {
format!(
"{}.{}",
&str_amount[0..str_amount.len() - decimals],
&str_amount[str_amount.len() - decimals..]
)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_format_units() {
assert_eq!(format_units(1_337u64.into(), 0), "1337");
assert_eq!(format_units(0u64.into(), 0), "0");
assert_eq!(format_units(0u64.into(), 1), "0.0");
assert_eq!(format_units(1u64.into(), 6), "0.000001");
assert_eq!(format_units(999_999u64.into(), 6), "0.999999");
assert_eq!(format_units(1_000_000u64.into(), 6), "1.000000");
assert_eq!(format_units(1_337_000u64.into(), 6), "1.337000");
assert_eq!(
format_units(1_337_000_004_200u64.into(), 6),
"1337000.004200"
)
}
}
2 changes: 2 additions & 0 deletions src/referral_maintenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ mod tests {
let expected = AppData {
version: "0.1.0".to_string(),
app_code: "CowSwap".to_string(),
environment: None,
metadata: Some(Metadata {
environment: None,
referrer: Some(Referrer {
Expand All @@ -299,6 +300,7 @@ mod tests {
.unwrap(),
version: "0.1.0".to_string(),
}),
quote: None,
}),
};
assert_eq!(referral, expected);
Expand Down

0 comments on commit 4c37f08

Please sign in to comment.