Skip to content

Commit 6ed8c40

Browse files
committed
Extract asset_identifier parser in CLI
Several endpoints require parsing input for and initializing asset identifiers. Extracted that logic into it's own mod. Also reformatted the input structure of asset_identifiers so the end user has less repetitive typing to do.
1 parent 88448ee commit 6ed8c40

File tree

5 files changed

+108
-160
lines changed

5 files changed

+108
-160
lines changed

cli/src/asset_identifier.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use error::InvalidInputError;
2+
use stellar_client::resources::AssetIdentifier;
3+
4+
pub fn from_str(s: &str) -> Result<AssetIdentifier, InvalidInputError> {
5+
let tokens: Vec<&str> = s.split('-').collect();
6+
if tokens.len() > 2 {
7+
return Err(InvalidInputError::from_str(
8+
"Asset identifier not of the form <asset_code>-<asset_issuer>",
9+
));
10+
}
11+
12+
match (tokens.get(0), tokens.get(1)) {
13+
(Some(&"XLM"), None) => return Ok(AssetIdentifier::Native),
14+
(Some(code), Some(issuer)) if code.len() <= 4 => {
15+
return Ok(AssetIdentifier::alphanum4(code, issuer))
16+
}
17+
(Some(code), Some(issuer)) if code.len() <= 12 => {
18+
return Ok(AssetIdentifier::alphanum12(code, issuer))
19+
}
20+
_ => {
21+
return Err(InvalidInputError::from_str(
22+
"Asset identifier not of the form <asset_code>-<asset_issuer>",
23+
))
24+
}
25+
}
26+
}
27+
28+
#[cfg(test)]
29+
mod tests {
30+
use super::*;
31+
32+
#[test]
33+
fn it_can_parse_asset_identifiers() {
34+
assert_eq!(from_str("XLM").unwrap(), AssetIdentifier::Native);
35+
assert_eq!(
36+
from_str("fox-123ABC").unwrap(),
37+
AssetIdentifier::alphanum4("fox", "123ABC")
38+
);
39+
assert_eq!(
40+
from_str("starfox-123ABC").unwrap(),
41+
AssetIdentifier::alphanum12("starfox", "123ABC")
42+
);
43+
}
44+
45+
#[test]
46+
fn it_returns_appropriate_errors() {
47+
assert!(from_str("fox-123-abs").is_err());
48+
assert!(from_str("foxisareallycoolanimal-123").is_err());
49+
}
50+
}

cli/src/error.rs

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ pub struct InvalidInputError {
2121
details: String,
2222
}
2323

24+
impl InvalidInputError {
25+
pub fn from_str(details: &str) -> InvalidInputError {
26+
InvalidInputError {
27+
details: details.to_string(),
28+
}
29+
}
30+
}
31+
2432
impl fmt::Display for InvalidInputError {
2533
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2634
write!(f, "{}", self.details)

cli/src/main.rs

+21-90
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use error::CliError;
1111

1212
mod account;
1313
mod assets;
14+
mod asset_identifier;
1415
mod cursor;
1516
mod effects;
1617
mod error;
@@ -150,42 +151,18 @@ fn build_app<'a, 'b>() -> App<'a, 'b> {
150151
SubCommand::with_name("details")
151152
.about("Fetch details about bids and asks for a given asset pair")
152153
.arg(
153-
Arg::with_name("base_asset_type")
154-
.long("base_asset_type")
154+
Arg::with_name("base")
155+
.long("base")
155156
.takes_value(true)
156157
.required(true)
157-
.help("Specifies base_asset_type for orderbook to return"),
158+
.help("Specifies base_asset for orderbook to return. format: <asset_code>-<asset_issuer>, or xlm if lumens"),
158159
)
159160
.arg(
160-
Arg::with_name("base_asset_code")
161-
.long("base_asset_code")
162-
.takes_value(true)
163-
.help("Not required for XLM"),
164-
)
165-
.arg(
166-
Arg::with_name("base_asset_issuer")
167-
.long("base_asset_issuer")
168-
.takes_value(true)
169-
.help("Not required for XLM"),
170-
)
171-
.arg(
172-
Arg::with_name("counter_asset_type")
173-
.long("counter_asset_type")
161+
Arg::with_name("counter")
162+
.long("counter")
174163
.takes_value(true)
175164
.required(true)
176-
.help("Specifies counter_asset_type for orderbook to return"),
177-
)
178-
.arg(
179-
Arg::with_name("counter_asset_code")
180-
.long("counter_asset_code")
181-
.takes_value(true)
182-
.help("Not required for XLM"),
183-
)
184-
.arg(
185-
Arg::with_name("counter_asset_issuer")
186-
.long("counter_asset_issuer")
187-
.takes_value(true)
188-
.help("Not required for XLM"),
165+
.help("Specifies counter_asset for orderbook to return. format: <asset_code>-<asset_issuer>, or xlm if lumens"),
189166
)
190167
.arg(
191168
Arg::with_name("limit")
@@ -337,40 +314,18 @@ fn build_app<'a, 'b>() -> App<'a, 'b> {
337314
SubCommand::with_name("all")
338315
.about("Fetch all trades")
339316
.arg(
340-
Arg::with_name("base_asset_type")
341-
.long("base_asset_type")
342-
.takes_value(true)
343-
.help("Filters trades with a base_asset_type"),
344-
)
345-
.arg(
346-
Arg::with_name("base_asset_code")
347-
.long("base_asset_code")
348-
.takes_value(true)
349-
.help("Filters trades with a base_asset_code. Not required for XLM"),
350-
)
351-
.arg(
352-
Arg::with_name("base_asset_issuer")
353-
.long("base_asset_issuer")
354-
.takes_value(true)
355-
.help("Filters trades with a base_asset_issuer. Not required for XLM"),
356-
)
357-
.arg(
358-
Arg::with_name("counter_asset_type")
359-
.long("counter_asset_type")
360-
.takes_value(true)
361-
.help("Filters trades with a counter_asset_type"),
362-
)
363-
.arg(
364-
Arg::with_name("counter_asset_code")
365-
.long("counter_asset_code")
317+
Arg::with_name("base")
318+
.long("base")
366319
.takes_value(true)
367-
.help("Filters trades with a counter_asset_code. Not required for XLM"),
320+
.required(true)
321+
.help("Filters trades with a given base_asset. format: <asset_code>-<asset_issuer>, or xlm if lumens"),
368322
)
369323
.arg(
370-
Arg::with_name("counter_asset_issuer")
371-
.long("counter_asset_issuer")
324+
Arg::with_name("counter")
325+
.long("counter")
372326
.takes_value(true)
373-
.help("Filters trades with a counter_asset_issuer. Not required for XLM"),
327+
.required(true)
328+
.help("Filters trades with a given counter_asset. format: <asset_code>-<asset_issuer>, or xlm if lumens"),
374329
)
375330
.arg(
376331
Arg::with_name("offer_id")
@@ -405,42 +360,18 @@ fn build_app<'a, 'b>() -> App<'a, 'b> {
405360
.help("Segment duration in format <number><unit> where units are s, m, h, d. ie: 10h == 10 hours"),
406361
)
407362
.arg(
408-
Arg::with_name("base_asset_type")
409-
.long("base_asset_type")
363+
Arg::with_name("base")
364+
.long("base")
410365
.takes_value(true)
411366
.required(true)
412-
.help("Filters trades with a base_asset_type"),
413-
)
414-
.arg(
415-
Arg::with_name("base_asset_code")
416-
.long("base_asset_code")
417-
.takes_value(true)
418-
.help("Filters trades with a base_asset_code. Not required for XLM"),
367+
.help("Filters trades with a given base_asset. format: <asset_code>-<asset_issuer>, or xlm if lumens"),
419368
)
420369
.arg(
421-
Arg::with_name("base_asset_issuer")
422-
.long("base_asset_issuer")
423-
.takes_value(true)
424-
.help("Filters trades with a base_asset_issuer. Not required for XLM"),
425-
)
426-
.arg(
427-
Arg::with_name("counter_asset_type")
428-
.long("counter_asset_type")
370+
Arg::with_name("counter")
371+
.long("counter")
429372
.takes_value(true)
430373
.required(true)
431-
.help("Filters trades with a counter_asset_type"),
432-
)
433-
.arg(
434-
Arg::with_name("counter_asset_code")
435-
.long("counter_asset_code")
436-
.takes_value(true)
437-
.help("Filters trades with a counter_asset_code. Not required for XLM"),
438-
)
439-
.arg(
440-
Arg::with_name("counter_asset_issuer")
441-
.long("counter_asset_issuer")
442-
.takes_value(true)
443-
.help("Filters trades with a counter_asset_issuer. Not required for XLM"),
374+
.help("Filters trades with a given counter_asset. format: <asset_code>-<asset_issuer>, or xlm if lumens"),
444375
)
445376
),
446377
)

cli/src/orderbook.rs

+12-26
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,22 @@
1+
use asset_identifier;
12
use clap::ArgMatches;
23
use error::Result;
34
use fmt::{Formatter, Simple};
4-
use stellar_client::{endpoint::{orderbook, Limit}, resources::AssetIdentifier, sync::Client};
5+
use stellar_client::{endpoint::{orderbook, Limit}, sync::Client};
56

67
pub fn details(client: &Client, matches: &ArgMatches) -> Result<()> {
78
let endpoint = {
8-
let base_asset_type = matches
9-
.value_of("base_asset_type")
10-
.expect("Base asset type is a required field");
11-
let counter_asset_type = matches
12-
.value_of("counter_asset_type")
13-
.expect("Counter asset type is a required field");
14-
let base_asset_code = matches
15-
.value_of("base_asset_code")
16-
.map(|code| code.to_string());
17-
let base_asset_issuer = matches
18-
.value_of("base_asset_issuer")
19-
.map(|issuer| issuer.to_string());
20-
let counter_asset_code = matches
21-
.value_of("counter_asset_code")
22-
.map(|code| code.to_string());
23-
let counter_asset_issuer = matches
24-
.value_of("counter_asset_issuer")
25-
.map(|issuer| issuer.to_string());
26-
let base_asset = AssetIdentifier::new(base_asset_type, base_asset_code, base_asset_issuer)?;
27-
let counter_asset =
28-
AssetIdentifier::new(counter_asset_type, counter_asset_code, counter_asset_issuer)?;
29-
let mut endpoint = orderbook::Details::for_asset_pair(base_asset, counter_asset);
9+
let base_str = matches
10+
.value_of("base")
11+
.expect("Base asset is a required field");
12+
let counter_str = matches
13+
.value_of("counter")
14+
.expect("Counter asset is a required field");
15+
let base = asset_identifier::from_str(base_str)?;
16+
let counter = asset_identifier::from_str(counter_str)?;
17+
let mut endpoint = orderbook::Details::for_asset_pair(base, counter);
3018
if let Some(limit) = matches.value_of("limit") {
31-
let limit = limit
32-
.parse::<u32>()
33-
.map_err(|_| String::from("Limitshould be a valid u32 integer"))?;
19+
let limit = limit.parse::<u32>()?;
3420
endpoint = endpoint.with_limit(limit);
3521
}
3622
endpoint

cli/src/trades.rs

+17-44
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use asset_identifier;
12
use chrono::{DateTime, Utc};
23
use clap::ArgMatches;
34
use error::Result;
45
use fmt::{Formatter, Simple};
56
use resolution::Resolution;
6-
use stellar_client::{endpoint::trade, resources::AssetIdentifier, sync::{self, Client}};
7+
use stellar_client::{endpoint::trade, sync::{self, Client}};
78
use super::{cursor, ordering, pager::Pager};
89

910
pub fn all(client: &Client, matches: &ArgMatches) -> Result<()> {
@@ -18,27 +19,12 @@ pub fn all(client: &Client, matches: &ArgMatches) -> Result<()> {
1819
.map_err(|_| String::from("Offer Id should be a valid u32 integer"))?;
1920
endpoint = endpoint.with_offer_id(offer_id);
2021
};
21-
if let (Some(base_asset_type), Some(counter_asset_type)) = (
22-
matches.value_of("base_asset_type"),
23-
matches.value_of("counter_asset_type"),
24-
) {
25-
let base_asset_code = matches
26-
.value_of("base_asset_code")
27-
.map(|code| code.to_string());
28-
let base_asset_issuer = matches
29-
.value_of("base_asset_issuer")
30-
.map(|issuer| issuer.to_string());
31-
let counter_asset_code = matches
32-
.value_of("counter_asset_code")
33-
.map(|code| code.to_string());
34-
let counter_asset_issuer = matches
35-
.value_of("counter_asset_issuer")
36-
.map(|issuer| issuer.to_string());
37-
let base_asset =
38-
AssetIdentifier::new(base_asset_type, base_asset_code, base_asset_issuer)?;
39-
let counter_asset =
40-
AssetIdentifier::new(counter_asset_type, counter_asset_code, counter_asset_issuer)?;
41-
endpoint = endpoint.with_asset_pair(base_asset, counter_asset);
22+
if let (Some(base_str), Some(counter_str)) =
23+
(matches.value_of("base"), matches.value_of("counter"))
24+
{
25+
let base = asset_identifier::from_str(base_str)?;
26+
let counter = asset_identifier::from_str(counter_str)?;
27+
endpoint = endpoint.with_asset_pair(base, counter);
4228
}
4329
endpoint = pager.assign(endpoint);
4430
endpoint = cursor::assign_from_arg(matches, endpoint);
@@ -75,28 +61,15 @@ pub fn aggregations(client: &Client, matches: &ArgMatches) -> Result<()> {
7561
let pager = Pager::from_arg(&matches);
7662

7763
let endpoint = {
78-
let base_asset_type = matches
79-
.value_of("base_asset_type")
80-
.expect("Base asset type is a required field");
81-
let counter_asset_type = matches
82-
.value_of("counter_asset_type")
83-
.expect("Counter asset type is a required field");
84-
let base_asset_code = matches
85-
.value_of("base_asset_code")
86-
.map(|code| code.to_string());
87-
let base_asset_issuer = matches
88-
.value_of("base_asset_issuer")
89-
.map(|issuer| issuer.to_string());
90-
let counter_asset_code = matches
91-
.value_of("counter_asset_code")
92-
.map(|code| code.to_string());
93-
let counter_asset_issuer = matches
94-
.value_of("counter_asset_issuer")
95-
.map(|issuer| issuer.to_string());
96-
let base_asset = AssetIdentifier::new(base_asset_type, base_asset_code, base_asset_issuer)?;
97-
let counter_asset =
98-
AssetIdentifier::new(counter_asset_type, counter_asset_code, counter_asset_issuer)?;
99-
let mut endpoint = trade::Aggregations::new(&base_asset, &counter_asset);
64+
let base_str = matches
65+
.value_of("base")
66+
.expect("Base asset is a required field");
67+
let counter_str = matches
68+
.value_of("counter")
69+
.expect("Counter asset is a required field");
70+
let base = asset_identifier::from_str(base_str)?;
71+
let counter = asset_identifier::from_str(counter_str)?;
72+
let mut endpoint = trade::Aggregations::new(&base, &counter);
10073
let resolution = matches
10174
.value_of("resolution")
10275
.expect("Resolution is a required field");

0 commit comments

Comments
 (0)