Parsing command-line arguments

AWS is divided by regions, where each has its own endpoint to connect to services. Our tool will support two arguments to set a region and endpoint:

.arg(
Arg::with_name("region")
.long("region")
.value_name("REGION")
.help("Sets a region")
.takes_value(true),
)
.arg(
Arg::with_name("endpoint")
.long("endpoint-url")
.value_name("URL")
.help("Sets an endpoint url")
.takes_value(true),
)

We add both to App instance. The tool will support two commands to add a new item and to print all items. The first subcommand is add and it expects three arguments: USER_ID, LONGITUDE, and LATITUDE:

.subcommand(SubCommand::with_name(CMD_ADD).about("add geo record to the table")
.arg(Arg::with_name("USER_ID")
.help("Sets the id of a user")
.required(true)
.index(1))
.arg(Arg::with_name("LATITUDE")
.help("Sets a latitudelongitude of location")
.required(true)
.index(2))
.arg(Arg::with_name("LONGITUDE")
.help("Sets a longitude of location")
.required(true)
.index(3)))

The list subcommand requires USER_ID in arguments only:

.subcommand(SubCommand::with_name(CMD_LIST).about("print all records for the user")
.arg(Arg::with_name("USER_ID")
.help("User if to filter records")
.required(true)
.index(1)))

Add all of the preceding code to the main function. We can use these arguments to create a Region instance that we can use for a connection with DynamoDB:

let region = matches.value_of("endpoint").map(|endpoint| {
Region::Custom {
name: "custom".into(),
endpoint: endpoint.into(),
}
}).ok_or_else(|| format_err!("Region not set"))
.or_else(|_| {
matches.value_of("region")
.unwrap_or("us-east-1")
.parse()
})?;

The code works according to the following logic: if a user sets the --endpoint-url parameter, we create a Region with a custom name and provide an endpoint value. If endpoint is not set, we try to parse the --region parameter to the Region instance, or just use the us-east-1 value by default.

AWS takes the region value seriously, and if you create a table in one region, you can't access that table from another region. We used a custom name for the region, but for production tools, it's better to use the ~/.aws/config file or provide the flexibility to customize these settings.

Now, we can use the Region value to create a DynamoDbClient instance:

let client = DynamoDbClient::new(region);

The DynamoDbClient struct is used for sending queries to our DynamoDB instance. We will use this instance in the implementation of our commands. Do you remember the match expression that parses command-line arguments? Add this implementation for the add subcommand first, which puts a new item in a table, as follows:

(CMD_ADD, Some(matches)) => {
let user_id = matches.value_of("USER_ID").unwrap().to_owned();
let timestamp = Utc::now().to_string();
let latitude = matches.value_of("LATITUDE").unwrap().to_owned();
let longitude = matches.value_of("LONGITUDE").unwrap().to_owned();
let location = Location { user_id, timestamp, latitude, longitude };
add_location(&client, location)?;
}

The implementation is simple—we extract all provided arguments, generate a timestamp using the Utc::now call, and convert it into a String type in the ISO-8601 format. Lastly, we fill the Location instance and call the add_location function that we declared before.

Have you ever wondered why databases use the ISO-8601 format to represent dates, which look like YEAR-MONTH-DATE HOUR:MINUTE:SECOND? That's because dates stored in strings in this format are ordered chronologically if sorted alphabetically. It's very convenient: you can sort dates to get the earliest on top and the latest at the bottom.

We still need to implement the list subcommand:

(CMD_LIST, Some(matches)) => {
let user_id = matches.value_of("USER_ID").unwrap().to_owned();
let locations = list_locations(&client, user_id)?;
for location in locations {
println!("{:?}", location);
}
}

This command extracts USER_ID arguments and calls the list_locations function with the provided user_id value. Finally, we iterate over all locations and print them to the Terminal.

The implementation is finished and we can try it now.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.31.165