use crate::field_mapper::base::LeadingCommaType;
use crate::FieldDef;
pub enum FieldGroup<'a> {
LeadingRequired {
fields: Vec<FieldDef<'a>>,
comma_type: LeadingCommaType,
},
LeadingFailRequired {
fields: Vec<FieldDef<'a>>,
comma_type: LeadingCommaType,
},
TrailingRequired {
fields: Vec<FieldDef<'a>>,
comma_type: LeadingCommaType,
},
LeadingOptional {
field: FieldDef<'a>,
comma_type: LeadingCommaType,
},
FollowingOptional {
field: FieldDef<'a>,
comma_type: LeadingCommaType,
},
TrailingOptional {
field: FieldDef<'a>,
comma_type: LeadingCommaType,
},
}
impl<'a> FieldGroup<'a> {
pub fn is_optional(&self) -> bool {
matches!(
self,
FieldGroup::LeadingOptional { .. }
| FieldGroup::FollowingOptional { .. }
| FieldGroup::TrailingOptional { .. }
)
}
pub fn len(&self) -> usize {
match self {
Self::LeadingRequired { fields, .. }
| Self::LeadingFailRequired { fields, .. }
| Self::TrailingRequired { fields, .. } => fields.len(),
_ => 1,
}
}
}
pub struct FieldGroupList<'a> {
pub groups: Vec<FieldGroup<'a>>,
pub first_required: usize,
pub is_all_required: bool,
}
impl<'a> FieldGroupList<'a> {
pub fn from<T>(fields: T) -> Self
where
T: IntoIterator<Item=&'a FieldDef<'a>> + Clone,
{
let mut groups = Vec::new();
let mut current_group = Vec::new();
let mut has_required = false; let mut has_optional = false; let mut is_first_required_group = true;
for field in fields.clone().into_iter() {
if field.struct_field.is_option() {
if !current_group.is_empty() {
if !has_required {
groups.push(FieldGroup::LeadingRequired {
fields: current_group,
comma_type: LeadingCommaType::NoLeading,
});
has_required = true;
} else if is_first_required_group {
groups.push(FieldGroup::LeadingFailRequired {
fields: current_group,
comma_type: LeadingCommaType::CheckedLeading,
});
is_first_required_group = false;
} else {
groups.push(FieldGroup::TrailingRequired {
fields: current_group,
comma_type: LeadingCommaType::Leading,
});
}
current_group = Vec::new();
}
if !has_required && !has_optional {
groups.push(FieldGroup::LeadingOptional {
field: field.clone(),
comma_type: LeadingCommaType::NoLeading,
});
} else if !has_required {
groups.push(FieldGroup::FollowingOptional {
field: field.clone(),
comma_type: LeadingCommaType::CheckedLeading,
});
} else {
groups.push(FieldGroup::TrailingOptional {
field: field.clone(),
comma_type: LeadingCommaType::Leading,
});
}
has_optional = true;
} else {
current_group.push(field.clone());
}
}
if !current_group.is_empty() {
if !has_required {
groups.push(FieldGroup::LeadingRequired {
fields: current_group,
comma_type: LeadingCommaType::NoLeading,
});
} else if is_first_required_group {
groups.push(FieldGroup::LeadingFailRequired {
fields: current_group,
comma_type: LeadingCommaType::CheckedLeading,
});
} else {
groups.push(FieldGroup::TrailingRequired {
fields: current_group,
comma_type: LeadingCommaType::Leading,
});
}
}
let is_all_required = fields.into_iter().all(|f| !f.struct_field.is_option());
let first_required = {
let mut first_required_index = groups.len();
for (index, group) in groups.iter().enumerate() {
if matches!(
group,
FieldGroup::LeadingRequired { .. }
| FieldGroup::LeadingFailRequired{..}
| FieldGroup::TrailingRequired{..}
) {
first_required_index = index;
break;
}
}
first_required_index
};
Self {
groups,
first_required,
is_all_required,
}
}
}