fn generate_update_expressions()

in src/data.rs [499:619]


fn generate_update_expressions(
    action_type: UpdateActionType,
    given_expression: &str,
) -> GeneratedUpdateParams {
    let mut expression: String = String::from("");
    let mut names = HashMap::<String, String>::new();
    let mut vals = HashMap::<String, AttributeValue>::new();

    match action_type {
        UpdateActionType::Set => {
            /*
            BNF style syntax of UpdateExpression SET action is below, where "function" is one of "list_append" or "if_not_exists".
                set-action ::=
                    path = value
                value ::=
                    operand
                    | operand '+' operand
                    | operand '-' operand
                operand ::=
                    path | function
            */
            check_update_expression_compatibility(given_expression);

            let statements: Vec<&str> = given_expression.split(',').map(|x| x.trim()).collect();
            debug!("given_expression splitted by ',': {:?}", statements);

            expression.push_str("SET ");

            for (i, statement) in statements.into_iter().enumerate() {
                debug!("parsing a statement: {}", &statement);
                let path_and_value: Vec<&str> = statement.split('=').map(|x| x.trim()).collect();
                if path_and_value.len() != 2 {
                    error!(
                        "failed to parse a statement '{}' in 'path = value' style.",
                        &statement
                    );
                    std::process::exit(1);
                };
                let left_hand: &str = path_and_value[0];
                let right_hand: &str = path_and_value[1];
                debug!("path: {}, value: {}", &left_hand, &right_hand);

                debug!("left hand ... {:#?}", left_hand);
                let name_placeholder = String::from("#DYNEIN_ATTRNAME") + &i.to_string();
                if i > 0 {
                    expression.push_str(", ")
                };
                expression.push_str(&name_placeholder);
                expression.push_str(" = ");
                names.insert(name_placeholder.clone(), String::from(left_hand));

                debug!("right hand ... {:#?}", right_hand);
                let val_placeholder = String::from(":DYNEIN_ATTRVAL") + &i.to_string();
                // TODO: currently --set 'thatdate = "2020-02-02"' doesn't work bacause of "-". Need to use more smart parser.
                // TODO: support --set 'listAttr = [1,2]'. Currently mistakenly parse/split it by "," as ["listAttr = [1" and "2]".
                let right_hand_operands: Vec<&str> = right_hand
                    .split(|c| c == '+' || c == '-')
                    .map(|x| x.trim())
                    .collect();
                if right_hand_operands.len() == 1 {
                    // --set 'Attr = 123', where right_hand_operands = ["val"], of which length is 1.
                    expression.push_str(val_placeholder.as_str());
                    vals.insert(
                        val_placeholder,
                        str_to_attrval(right_hand).unwrap_or_else(|_| {
                            panic!(
                                "failed to parse right hand object '{}' into AttributeValue.",
                                &right_hand
                            )
                        }),
                    );
                } else if right_hand_operands.len() == 2 && right_hand_operands[0] == left_hand {
                    // --set 'Attr = Attr + 100', where right_hand_operands = ["val", 100], of which length is 2.
                    expression.push_str(&name_placeholder);
                    if right_hand.contains('+') {
                        expression.push_str(" + ")
                    } else if right_hand.contains('-') {
                        expression.push_str(" - ")
                    };
                    expression.push_str(val_placeholder.as_str());
                    vals.insert(
                        val_placeholder,
                        str_to_attrval(right_hand_operands[1]).unwrap_or_else(|_| {
                            panic!(
                                "failed to parse right hand object '{}' into AttributeValue.",
                                &right_hand_operands[1]
                            )
                        }),
                    );
                } else {
                    error!("failed to parse a right hand statement '{}'. Valid syntax would be: 'Attr = \"val\"', or 'Attr = Attr + 100'", &right_hand);
                    std::process::exit(1);
                }
            }
        }
        UpdateActionType::Remove => {
            // NOTE: REMOVE action for list elements is not supported and currently 'LisAtt[1]' is recognized as one token.
            check_update_expression_compatibility(given_expression);
            let mut returning_attributes: Vec<String> = vec![];
            let attributes: Vec<&str> = given_expression.split(',').map(|x| x.trim()).collect();
            debug!("given_expression splitted by ',': {:?}", attributes);
            for (i, attr) in attributes.into_iter().enumerate() {
                let placeholder = String::from("#DYNEIN_ATTRNAME") + &i.to_string();
                returning_attributes.push(placeholder.clone());
                names.insert(placeholder, String::from(attr));
            }
            expression.push_str("REMOVE ");
            expression.push_str(&returning_attributes.join(","));
        }
    }; // match action_type

    debug!("generated UpdateExpression: {:?}", expression);
    debug!("generated ExpressionAttributeNames: {:?}", names);
    debug!("generated ExpressionAttributeValues: {:?}", vals);

    GeneratedUpdateParams {
        exp: Some(expression),
        names: if names.is_empty() { None } else { Some(names) },
        vals: if vals.is_empty() { None } else { Some(vals) },
    }
}