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) },
}
}