fn append_sort_key_expression()

in src/data.rs [980:1127]


fn append_sort_key_expression(
    sort_key: Option<app::Key>,
    partition_key_expression: &str,
    sort_key_expression: &str,
    mut names: HashMap<String, String>,
    mut vals: HashMap<String, AttributeValue>,
) -> Result<GeneratedQueryParams, DyneinQueryParamsError> {
    // Check if the target table/index key schema has sort key. If there's no sort key definition, return with Err immediately.
    let (sk_name, sk_type) = match sort_key {
        Some(sk) => (sk.clone().name, sk.kind.to_string()),
        None => return Err(DyneinQueryParamsError::NoSortKeyDefined),
    };

    // Start building KeyConditionExpression. dynein automatically set placeholders, so currently it would be:
    //   "#DYNEIN_PKNAME = :DYNEIN_PKVAL AND "
    let mut built = format!("{} AND ", partition_key_expression);
    debug!(
        "Start building KeyConditionExpression. Currently built: '{}'",
        &built
    );

    // iterate over splitted tokens and build expression and mappings.
    let mut iter = sort_key_expression.trim().split_whitespace();
    // Query API https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-KeyConditionExpression
    match iter.next() {
        // sortKeyName = :sortkeyval - true if the sort key value is equal to :sortkeyval.
        Some("=") | Some("==") => {
            let target_val = iter.next().unwrap();
            debug!(
                "Equal sign is detected in sort key expression for the value: '{}'",
                &target_val
            );
            built.push_str("#DYNEIN_SKNAME = :DYNEIN_SKVAL");
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL"),
                build_attrval_scalar(&sk_type, &String::from(target_val)),
            );
        }
        // sortKeyName <= :sortkeyval - true if the sort key value is less than or equal to :sortkeyval.
        Some("<=") => {
            let target_val = iter.next().unwrap();
            debug!(
                "Less than equal sign is detected in sort key expression for the value: '{}'",
                &target_val
            );
            built.push_str("#DYNEIN_SKNAME <= :DYNEIN_SKVAL");
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL"),
                build_attrval_scalar(&sk_type, &String::from(target_val)),
            );
        }
        // sortKeyName < :sortkeyval - true if the sort key value is less than :sortkeyval.
        Some("<") => {
            let target_val = iter.next().unwrap();
            debug!(
                "Less than sign is detected in sort key expression for the value: '{}'",
                &target_val
            );
            built.push_str("#DYNEIN_SKNAME < :DYNEIN_SKVAL");
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL"),
                build_attrval_scalar(&sk_type, &String::from(target_val)),
            );
        }
        // sortKeyName >= :sortkeyval - true if the sort key value is greater than or equal to :sortkeyval.
        Some(">=") => {
            let target_val = iter.next().unwrap();
            debug!(
                "Greater than equal sign is detected in sort key expression for the value: '{}'",
                &target_val
            );
            built.push_str("#DYNEIN_SKNAME >= :DYNEIN_SKVAL");
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL"),
                build_attrval_scalar(&sk_type, &String::from(target_val)),
            );
        }
        // sortKeyName > :sortkeyval - true if the sort key value is greater than :sortkeyval.
        Some(">") => {
            let target_val = iter.next().unwrap();
            debug!(
                "Greater than sign is detected in sort key expression for the value: '{}'",
                &target_val
            );
            built.push_str("#DYNEIN_SKNAME > :DYNEIN_SKVAL");
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL"),
                build_attrval_scalar(&sk_type, &String::from(target_val)),
            );
        }
        // begins_with ( sortKeyName, :sortkeyval ) - true if the sort key value begins with a particular operand.
        // You cannot use this function with a sort key that is of type Number.
        Some("begins_with") | Some("BEGINS_WITH") => {
            let target_val = iter.next().unwrap();
            debug!(
                "`begins_with` is detected in sort key expression for the value: '{}'",
                &target_val
            );
            built.push_str("begins_with(#DYNEIN_SKNAME, :DYNEIN_SKVAL)"); // the function name `begins_with` is case-sensitive.
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL"),
                build_attrval_scalar(&sk_type, &String::from(target_val)),
            );
        }
        // sortKeyName BETWEEN :sortkeyval1 AND :sortkeyval2 - true if the sort key value is greater than or equal to :sortkeyval1, and less than or equal to :sortkeyval2.
        Some("between") | Some("BETWEEN") => {
            debug!("`between` is detected in sort key expression.");
            let from = iter.next().unwrap();
            match iter.next() {
                Some("and") | Some("AND") => (),
                _ => {
                    println!("ERROR: between syntax error. e.g. 'BETWEEN 10 AND 99'.");
                    std::process::exit(1)
                }
            }
            let to = iter.next().unwrap();
            debug!("Parsed from/to values: between '{}' and '{}'.", &from, &to);

            built.push_str("#DYNEIN_SKNAME BETWEEN :DYNEIN_SKVAL_FROM AND :DYNEIN_SKVAL_TO");
            names.insert(String::from("#DYNEIN_SKNAME"), sk_name);
            vals.insert(
                String::from(":DYNEIN_SKVAL_FROM"),
                build_attrval_scalar(&sk_type, &String::from(from)),
            );
            vals.insert(
                String::from(":DYNEIN_SKVAL_TO"),
                build_attrval_scalar(&sk_type, &String::from(to)),
            );
        }
        _ => return Err(DyneinQueryParamsError::InvalidSortKeyOption),
    };
    debug!(
        "Finished to build KeyConditionExpression. Currently built: '{}'",
        &built
    );

    Ok(GeneratedQueryParams {
        exp: Some(built),
        names: if names.is_empty() { None } else { Some(names) },
        vals: Some(vals),
    })
}