use cp_ast_core::constraint::{CharSetSpec, ConstraintId, RelationOp, SortOrder};
use cp_ast_core::operation::action::Action;
use cp_ast_core::operation::types::{
ConstraintDef, ConstraintDefKind, FillContent, LengthSpec, SumBoundDef, VarType,
};
use cp_ast_core::structure::{Literal, NodeId};
use crate::dto::{
ActionDto, CharSetSpecDto, ConstraintDefDto, FillContentDto, LengthSpecDto, LiteralDto,
SumBoundDefDto,
};
use crate::error::ConversionError;
pub fn serialize_action(action: &Action) -> Result<String, ConversionError> {
let dto = action_to_dto(action);
serde_json::to_string(&dto).map_err(ConversionError::from)
}
pub fn deserialize_action(json: &str) -> Result<Action, ConversionError> {
let dto: ActionDto = serde_json::from_str(json)?;
dto_to_action(&dto)
}
#[must_use]
pub fn action_to_dto(action: &Action) -> ActionDto {
match action {
Action::FillHole { target, fill } => ActionDto::FillHole {
target: node_id_str(*target),
fill: fill_content_to_dto(fill),
},
Action::ReplaceNode {
target,
replacement,
} => ActionDto::ReplaceNode {
target: node_id_str(*target),
replacement: fill_content_to_dto(replacement),
},
Action::AddConstraint { target, constraint } => ActionDto::AddConstraint {
target: node_id_str(*target),
constraint: constraint_def_to_dto(constraint),
},
Action::RemoveConstraint { constraint_id } => ActionDto::RemoveConstraint {
constraint_id: constraint_id.value().to_string(),
},
Action::IntroduceMultiTestCase {
count_var_name,
sum_bound,
} => ActionDto::IntroduceMultiTestCase {
count_var_name: count_var_name.clone(),
sum_bound: sum_bound.as_ref().map(sum_bound_def_to_dto),
},
Action::AddSlotElement {
parent,
slot_name,
element,
} => ActionDto::AddSlotElement {
parent: node_id_str(*parent),
slot_name: slot_name.clone(),
element: fill_content_to_dto(element),
},
Action::RemoveSlotElement {
parent,
slot_name,
child,
} => ActionDto::RemoveSlotElement {
parent: node_id_str(*parent),
slot_name: slot_name.clone(),
child: node_id_str(*child),
},
Action::AddSibling { target, element } => ActionDto::AddSibling {
target: node_id_str(*target),
element: fill_content_to_dto(element),
},
Action::AddChoiceVariant {
choice,
tag_value,
first_element,
} => ActionDto::AddChoiceVariant {
choice: node_id_str(*choice),
tag_value: literal_to_dto(tag_value),
first_element: fill_content_to_dto(first_element),
},
}
}
pub fn dto_to_action(dto: &ActionDto) -> Result<Action, ConversionError> {
match dto {
ActionDto::FillHole { target, fill } => Ok(Action::FillHole {
target: parse_node_id(target)?,
fill: dto_to_fill_content(fill)?,
}),
ActionDto::ReplaceNode {
target,
replacement,
} => Ok(Action::ReplaceNode {
target: parse_node_id(target)?,
replacement: dto_to_fill_content(replacement)?,
}),
ActionDto::AddConstraint { target, constraint } => Ok(Action::AddConstraint {
target: parse_node_id(target)?,
constraint: dto_to_constraint_def(constraint)?,
}),
ActionDto::RemoveConstraint { constraint_id } => Ok(Action::RemoveConstraint {
constraint_id: parse_constraint_id(constraint_id)?,
}),
ActionDto::IntroduceMultiTestCase {
count_var_name,
sum_bound,
} => Ok(Action::IntroduceMultiTestCase {
count_var_name: count_var_name.clone(),
sum_bound: sum_bound.as_ref().map(dto_to_sum_bound_def),
}),
ActionDto::AddSlotElement {
parent,
slot_name,
element,
} => Ok(Action::AddSlotElement {
parent: parse_node_id(parent)?,
slot_name: slot_name.clone(),
element: dto_to_fill_content(element)?,
}),
ActionDto::RemoveSlotElement {
parent,
slot_name,
child,
} => Ok(Action::RemoveSlotElement {
parent: parse_node_id(parent)?,
slot_name: slot_name.clone(),
child: parse_node_id(child)?,
}),
ActionDto::AddSibling { target, element } => Ok(Action::AddSibling {
target: parse_node_id(target)?,
element: dto_to_fill_content(element)?,
}),
ActionDto::AddChoiceVariant {
choice,
tag_value,
first_element,
} => Ok(Action::AddChoiceVariant {
choice: parse_node_id(choice)?,
tag_value: dto_to_literal(tag_value),
first_element: dto_to_fill_content(first_element)?,
}),
}
}
fn fill_content_to_dto(fc: &FillContent) -> FillContentDto {
match fc {
FillContent::Scalar { name, typ } => FillContentDto::Scalar {
name: name.clone(),
typ: var_type_str(typ),
},
FillContent::Array {
name,
element_type,
length,
} => FillContentDto::Array {
name: name.clone(),
element_type: var_type_str(element_type),
length: length_spec_to_dto(length),
},
FillContent::Repeat { count } => FillContentDto::Repeat {
count: length_spec_to_dto(count),
},
FillContent::Grid {
name,
rows,
cols,
cell_type,
} => FillContentDto::Grid {
name: name.clone(),
rows: length_spec_to_dto(rows),
cols: length_spec_to_dto(cols),
cell_type: var_type_str(cell_type),
},
FillContent::Section { label } => FillContentDto::Section {
label: label.clone(),
},
FillContent::OutputSingleValue { typ } => FillContentDto::OutputSingleValue {
typ: var_type_str(typ),
},
FillContent::OutputYesNo => FillContentDto::OutputYesNo,
FillContent::EdgeList { edge_count } => FillContentDto::EdgeList {
edge_count: length_spec_to_dto(edge_count),
},
FillContent::WeightedEdgeList {
edge_count,
weight_name,
weight_type,
} => FillContentDto::WeightedEdgeList {
edge_count: length_spec_to_dto(edge_count),
weight_name: weight_name.clone(),
weight_type: var_type_str(weight_type),
},
FillContent::QueryList { query_count } => FillContentDto::QueryList {
query_count: length_spec_to_dto(query_count),
},
FillContent::MultiTestCaseTemplate { count } => FillContentDto::MultiTestCaseTemplate {
count: length_spec_to_dto(count),
},
FillContent::GridTemplate {
name,
rows,
cols,
cell_type,
} => FillContentDto::GridTemplate {
name: name.clone(),
rows: length_spec_to_dto(rows),
cols: length_spec_to_dto(cols),
cell_type: var_type_str(cell_type),
},
}
}
fn dto_to_fill_content(dto: &FillContentDto) -> Result<FillContent, ConversionError> {
match dto {
FillContentDto::Scalar { name, typ } => Ok(FillContent::Scalar {
name: name.clone(),
typ: str_to_var_type(typ)?,
}),
FillContentDto::Array {
name,
element_type,
length,
} => Ok(FillContent::Array {
name: name.clone(),
element_type: str_to_var_type(element_type)?,
length: dto_to_length_spec(length)?,
}),
FillContentDto::Repeat { count } => Ok(FillContent::Repeat {
count: dto_to_length_spec(count)?,
}),
FillContentDto::Grid {
name,
rows,
cols,
cell_type,
} => Ok(FillContent::Grid {
name: name.clone(),
rows: dto_to_length_spec(rows)?,
cols: dto_to_length_spec(cols)?,
cell_type: str_to_var_type(cell_type)?,
}),
FillContentDto::Section { label } => Ok(FillContent::Section {
label: label.clone(),
}),
FillContentDto::OutputSingleValue { typ } => Ok(FillContent::OutputSingleValue {
typ: str_to_var_type(typ)?,
}),
FillContentDto::OutputYesNo => Ok(FillContent::OutputYesNo),
FillContentDto::EdgeList { edge_count } => Ok(FillContent::EdgeList {
edge_count: dto_to_length_spec(edge_count)?,
}),
FillContentDto::WeightedEdgeList {
edge_count,
weight_name,
weight_type,
} => Ok(FillContent::WeightedEdgeList {
edge_count: dto_to_length_spec(edge_count)?,
weight_name: weight_name.clone(),
weight_type: str_to_var_type(weight_type)?,
}),
FillContentDto::QueryList { query_count } => Ok(FillContent::QueryList {
query_count: dto_to_length_spec(query_count)?,
}),
FillContentDto::MultiTestCaseTemplate { count } => Ok(FillContent::MultiTestCaseTemplate {
count: dto_to_length_spec(count)?,
}),
FillContentDto::GridTemplate {
name,
rows,
cols,
cell_type,
} => Ok(FillContent::GridTemplate {
name: name.clone(),
rows: dto_to_length_spec(rows)?,
cols: dto_to_length_spec(cols)?,
cell_type: str_to_var_type(cell_type)?,
}),
}
}
fn length_spec_to_dto(ls: &LengthSpec) -> LengthSpecDto {
match ls {
LengthSpec::Fixed(n) => LengthSpecDto::Fixed { value: *n },
LengthSpec::RefVar(id) => LengthSpecDto::RefVar {
node_id: node_id_str(*id),
},
LengthSpec::Expr(s) => LengthSpecDto::Expr { expr: s.clone() },
}
}
fn dto_to_length_spec(dto: &LengthSpecDto) -> Result<LengthSpec, ConversionError> {
match dto {
LengthSpecDto::Fixed { value } => Ok(LengthSpec::Fixed(*value)),
LengthSpecDto::RefVar { node_id } => Ok(LengthSpec::RefVar(parse_node_id(node_id)?)),
LengthSpecDto::Expr { expr } => Ok(LengthSpec::Expr(expr.clone())),
}
}
fn constraint_def_to_dto(cd: &ConstraintDef) -> ConstraintDefDto {
match &cd.kind {
ConstraintDefKind::Range { lower, upper } => ConstraintDefDto::Range {
lower: lower.clone(),
upper: upper.clone(),
},
ConstraintDefKind::TypeDecl { typ } => ConstraintDefDto::TypeDecl {
typ: var_type_str(typ),
},
ConstraintDefKind::Relation { op, rhs } => ConstraintDefDto::Relation {
op: relation_op_str(*op),
rhs: rhs.clone(),
},
ConstraintDefKind::Distinct => ConstraintDefDto::Distinct,
ConstraintDefKind::Sorted { order } => ConstraintDefDto::Sorted {
order: sort_order_str(*order),
},
ConstraintDefKind::Property { tag } => ConstraintDefDto::Property { tag: tag.clone() },
ConstraintDefKind::SumBound { over_var, upper } => ConstraintDefDto::SumBound {
over_var: over_var.clone(),
upper: upper.clone(),
},
ConstraintDefKind::CharSet { charset } => ConstraintDefDto::CharSet {
charset: charset_spec_to_dto(charset),
},
ConstraintDefKind::StringLength { min, max } => ConstraintDefDto::StringLength {
min: min.clone(),
max: max.clone(),
},
ConstraintDefKind::Guarantee { description } => ConstraintDefDto::Guarantee {
description: description.clone(),
},
}
}
fn dto_to_constraint_def(dto: &ConstraintDefDto) -> Result<ConstraintDef, ConversionError> {
let kind = match dto {
ConstraintDefDto::Range { lower, upper } => ConstraintDefKind::Range {
lower: lower.clone(),
upper: upper.clone(),
},
ConstraintDefDto::TypeDecl { typ } => ConstraintDefKind::TypeDecl {
typ: str_to_var_type(typ)?,
},
ConstraintDefDto::Relation { op, rhs } => ConstraintDefKind::Relation {
op: str_to_relation_op(op)?,
rhs: rhs.clone(),
},
ConstraintDefDto::Distinct => ConstraintDefKind::Distinct,
ConstraintDefDto::Sorted { order } => ConstraintDefKind::Sorted {
order: str_to_sort_order(order)?,
},
ConstraintDefDto::Property { tag } => ConstraintDefKind::Property { tag: tag.clone() },
ConstraintDefDto::SumBound { over_var, upper } => ConstraintDefKind::SumBound {
over_var: over_var.clone(),
upper: upper.clone(),
},
ConstraintDefDto::CharSet { charset } => ConstraintDefKind::CharSet {
charset: dto_to_charset_spec(charset),
},
ConstraintDefDto::StringLength { min, max } => ConstraintDefKind::StringLength {
min: min.clone(),
max: max.clone(),
},
ConstraintDefDto::Guarantee { description } => ConstraintDefKind::Guarantee {
description: description.clone(),
},
};
Ok(ConstraintDef { kind })
}
fn charset_spec_to_dto(charset: &CharSetSpec) -> CharSetSpecDto {
match charset {
CharSetSpec::LowerAlpha => CharSetSpecDto::LowerAlpha,
CharSetSpec::UpperAlpha => CharSetSpecDto::UpperAlpha,
CharSetSpec::Alpha => CharSetSpecDto::Alpha,
CharSetSpec::Digit => CharSetSpecDto::Digit,
CharSetSpec::AlphaNumeric => CharSetSpecDto::AlphaNumeric,
CharSetSpec::Custom(chars) => CharSetSpecDto::Custom {
chars: chars.clone(),
},
CharSetSpec::Range(from, to) => CharSetSpecDto::Range {
from: *from,
to: *to,
},
}
}
fn dto_to_charset_spec(charset: &CharSetSpecDto) -> CharSetSpec {
match charset {
CharSetSpecDto::LowerAlpha => CharSetSpec::LowerAlpha,
CharSetSpecDto::UpperAlpha => CharSetSpec::UpperAlpha,
CharSetSpecDto::Alpha => CharSetSpec::Alpha,
CharSetSpecDto::Digit => CharSetSpec::Digit,
CharSetSpecDto::AlphaNumeric => CharSetSpec::AlphaNumeric,
CharSetSpecDto::Custom { chars } => CharSetSpec::Custom(chars.clone()),
CharSetSpecDto::Range { from, to } => CharSetSpec::Range(*from, *to),
}
}
fn sum_bound_def_to_dto(sb: &SumBoundDef) -> SumBoundDefDto {
SumBoundDefDto {
bound_var: sb.bound_var.clone(),
upper: sb.upper.clone(),
}
}
fn dto_to_sum_bound_def(dto: &SumBoundDefDto) -> SumBoundDef {
SumBoundDef {
bound_var: dto.bound_var.clone(),
upper: dto.upper.clone(),
}
}
fn literal_to_dto(l: &Literal) -> LiteralDto {
match l {
Literal::IntLit(v) => LiteralDto::IntLit { value: *v },
Literal::StrLit(s) => LiteralDto::StrLit { value: s.clone() },
}
}
fn dto_to_literal(dto: &LiteralDto) -> Literal {
match dto {
LiteralDto::IntLit { value } => Literal::IntLit(*value),
LiteralDto::StrLit { value } => Literal::StrLit(value.clone()),
}
}
fn node_id_str(id: NodeId) -> String {
id.value().to_string()
}
fn parse_node_id(s: &str) -> Result<NodeId, ConversionError> {
s.parse::<u64>()
.map(NodeId::from_raw)
.map_err(|_| ConversionError::InvalidId(s.to_owned()))
}
fn parse_constraint_id(s: &str) -> Result<ConstraintId, ConversionError> {
s.parse::<u64>()
.map(ConstraintId::from_raw)
.map_err(|_| ConversionError::InvalidId(s.to_owned()))
}
fn var_type_str(vt: &VarType) -> String {
match vt {
VarType::Int => "Int".to_owned(),
VarType::Str => "Str".to_owned(),
VarType::Char => "Char".to_owned(),
}
}
fn str_to_var_type(s: &str) -> Result<VarType, ConversionError> {
match s {
"Int" => Ok(VarType::Int),
"Str" => Ok(VarType::Str),
"Char" => Ok(VarType::Char),
_ => Err(ConversionError::UnknownVariant {
type_name: "VarType",
value: s.to_owned(),
}),
}
}
fn relation_op_str(op: RelationOp) -> String {
match op {
RelationOp::Lt => "Lt".to_owned(),
RelationOp::Le => "Le".to_owned(),
RelationOp::Gt => "Gt".to_owned(),
RelationOp::Ge => "Ge".to_owned(),
RelationOp::Eq => "Eq".to_owned(),
RelationOp::Ne => "Ne".to_owned(),
}
}
fn str_to_relation_op(s: &str) -> Result<RelationOp, ConversionError> {
match s {
"Lt" => Ok(RelationOp::Lt),
"Le" => Ok(RelationOp::Le),
"Gt" => Ok(RelationOp::Gt),
"Ge" => Ok(RelationOp::Ge),
"Eq" => Ok(RelationOp::Eq),
"Ne" => Ok(RelationOp::Ne),
_ => Err(ConversionError::UnknownVariant {
type_name: "RelationOp",
value: s.to_owned(),
}),
}
}
fn sort_order_str(o: SortOrder) -> String {
match o {
SortOrder::Ascending => "Ascending".to_owned(),
SortOrder::NonDecreasing => "NonDecreasing".to_owned(),
SortOrder::Descending => "Descending".to_owned(),
SortOrder::NonIncreasing => "NonIncreasing".to_owned(),
}
}
fn str_to_sort_order(s: &str) -> Result<SortOrder, ConversionError> {
match s {
"Ascending" => Ok(SortOrder::Ascending),
"NonDecreasing" => Ok(SortOrder::NonDecreasing),
"Descending" => Ok(SortOrder::Descending),
"NonIncreasing" => Ok(SortOrder::NonIncreasing),
_ => Err(ConversionError::UnknownVariant {
type_name: "SortOrder",
value: s.to_owned(),
}),
}
}