use super::alignment::Alignment;
use super::color::Color;
use super::coordonate::Coordonate;
use super::dimension::Dimension;
use super::dock::Dock;
use super::named_params_map::NamedParamsMap;
use super::size::Size;
use super::Error;
use crate::utils;
pub(super) enum ValueType<'a> {
Undetermined,
Bool(bool),
Integer(i32),
Percentage(f32),
Size(Size),
Alignment(Alignment),
Dock(Dock),
Color(Color),
List(Vec<Value<'a>>),
Dict(NamedParamsMap<'a>),
Dimension(Dimension),
Coordonate(Coordonate),
}
pub(crate) struct Value<'a> {
pub(super) param_name: &'a str,
pub(super) raw_data: &'a str,
pub(super) data_type: ValueType<'a>,
pub(super) validated: bool,
pub(super) start: usize,
pub(super) end: usize,
}
impl<'a> Value<'a> {
#[inline(always)]
pub(crate) fn get_start_pos(&self) -> usize {
self.start
}
#[inline(always)]
pub(crate) fn get_end_pos(&self) -> usize {
self.end
}
pub(crate) fn is_dict(&self) -> bool {
matches!(self.data_type, ValueType::Dict(_))
}
pub(crate) fn is_list(&self) -> bool {
matches!(self.data_type, ValueType::List(_))
}
pub(crate) fn is_value(&self) -> bool {
!matches!(self.data_type, ValueType::List(_) | ValueType::Dict(_))
}
pub(crate) fn get_bool(&mut self) -> Option<bool> {
if !self.is_value() {
return None;
}
if let ValueType::Bool(value) = &self.data_type {
return Some(*value);
}
if utils::equal_ignore_case("true", self.raw_data) {
self.data_type = ValueType::Bool(true);
return Some(true);
}
if utils::equal_ignore_case("false", self.raw_data) {
self.data_type = ValueType::Bool(false);
return Some(false);
}
None
}
pub(crate) fn get_i32(&mut self) -> Option<i32> {
if !self.is_value() {
return None;
}
if let ValueType::Integer(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = utils::to_i32(self.raw_data) {
self.data_type = ValueType::Integer(value);
return Some(value);
}
None
}
pub(crate) fn get_percentage(&mut self) -> Option<f32> {
if !self.is_value() {
return None;
}
if let ValueType::Percentage(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = utils::to_percentage(self.raw_data) {
self.data_type = ValueType::Percentage(value);
return Some(value);
}
None
}
pub(crate) fn get_dimension(&mut self) -> Option<Dimension> {
if !self.is_value() {
return None;
}
if let ValueType::Dimension(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = Dimension::from_str(self.raw_data) {
self.data_type = ValueType::Dimension(value);
return Some(value);
}
None
}
pub(crate) fn get_coordonate(&mut self) -> Option<Coordonate> {
if !self.is_value() {
return None;
}
if let ValueType::Coordonate(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = Coordonate::from_str(self.raw_data) {
self.data_type = ValueType::Coordonate(value);
return Some(value);
}
None
}
pub(crate) fn to_dock(&self) -> Dock {
if let ValueType::Dock(value) = &self.data_type {
return *value;
}
panic!("Invalid dock value: {}", self.raw_data);
}
pub(crate) fn get_alignment(&mut self) -> Option<Alignment> {
if !self.is_value() {
return None;
}
if let ValueType::Alignment(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = Alignment::from_hash(utils::compute_hash(self.raw_data)) {
self.data_type = ValueType::Alignment(value);
return Some(value);
}
None
}
pub(crate) fn get_dock(&mut self) -> Option<Dock> {
if !self.is_value() {
return None;
}
if let ValueType::Dock(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = Dock::from_hash(utils::compute_hash(self.raw_data)) {
self.data_type = ValueType::Dock(value);
return Some(value);
}
None
}
pub(crate) fn get_color(&mut self) -> Option<Color> {
if !self.is_value() {
return None;
}
if let ValueType::Color(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = Color::from_hash(utils::compute_hash(self.raw_data)) {
self.data_type = ValueType::Color(value);
return Some(value);
}
None
}
pub(crate) fn get_size(&mut self) -> Option<Size> {
if !self.is_value() {
return None;
}
if let ValueType::Size(value) = &self.data_type {
return Some(*value);
}
if let Some(value) = Size::from_str(self.raw_data) {
self.data_type = ValueType::Size(value);
return Some(value);
}
None
}
#[inline(always)]
pub(crate) fn get_string(&self) -> &str {
self.raw_data
}
pub(crate) fn get_dict(&mut self) -> Option<&mut NamedParamsMap<'a>> {
match &mut self.data_type {
ValueType::Dict(obj) => Some(obj),
_ => None,
}
}
pub(crate) fn get_list(&mut self) -> Option<&mut Vec<Value<'a>>> {
match &mut self.data_type {
ValueType::List(obj) => Some(obj),
_ => None,
}
}
fn validate_bool(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_bool().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a bool value (true or false) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_layout(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if let Some(value) = self.get_i32() {
if !(-30000..=30000).contains(&value) {
return Err(Error::new(
param_list,
format!("The value for parameter '{display_param_name}' should be between -30000 and 30000. Current value is {value} !").as_str(),
self.start,
self.end,
));
}
return Ok(());
}
if let Some(value) = self.get_percentage() {
if !(-300.0f32..=300.0f32).contains(&value) {
return Err(Error::new(
param_list,
format!("The value for parameter '{display_param_name}' should be between -300% and 300%. Current value is {value}% !").as_str(),
self.start,
self.end,
));
}
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting an integer value (e.g. '{display_param_name}: 10') or a percentage value (e.g. '{display_param_name}: 50%`) for parameter '{display_param_name}' but found '{}'",
self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_layout_size(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if let Some(value) = self.get_i32() {
if value < 0 {
return Err(Error::new(
param_list,
format!(
"Negative values are not permitted for parameter '{display_param_name}' -> you have used the following value: '{value}' !"
)
.as_str(),
self.start,
self.end,
));
}
if !(0..=30000).contains(&value) {
return Err(Error::new(
param_list,
format!("The value for parameter '{display_param_name}' should be a positive number smaller than 30000 --> you have used the following value: '{value}' !").as_str(),
self.start,
self.end,
));
}
return Ok(());
}
if let Some(value) = self.get_percentage() {
if !(-300.0f32..=300.0f32).contains(&value) {
if value < 0.0f32 {
return Err(Error::new(
param_list,
format!("Negative percentages are not permitted for parameter '{display_param_name}' -> you have used the following value: '{}' !", self.get_string()).as_str(),
self.start,
self.end,
));
}
return Err(Error::new(
param_list,
format!("The percentage value for parameter '{display_param_name}' should be between -300% and 300% -> you have used the following percentage: '{value}%' !").as_str(),
self.start,
self.end,
));
}
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting an integer value (e.g. '{display_param_name}: 10') or a percentage value (e.g. '{display_param_name}: 50%`) for parameter '{display_param_name}' but found '{}'",
self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_flags(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.is_list() {
return Ok(());
}
if self.is_dict() {
return Err(Error::new(
param_list,
format!(
"Expecting a flag value for parameter '{display_param_name}' (a string, a word or a list [...]), but got parameter list '{{...}}'"
)
.as_str(),
self.start,
self.end,
));
}
let buf = self.raw_data.as_bytes();
let mut v: Vec<Value> = Vec::with_capacity(8);
let mut pos = 0;
let len = buf.len();
while pos < len {
pos = utils::skip_spaces(buf, pos);
if !utils::is_word_character(buf[pos]) {
return Err(Error::new(
param_list,
format!(
"Invalid flag format for parameter '{display_param_name}'. Expecting a flags name formed out of th following characters: 0-9, a-z, A-Z and underline."
)
.as_str(),
self.start+pos,
self.start+pos+1,
));
}
let next = utils::skip_words(buf, pos);
v.push(Value {
param_name: "",
raw_data: &self.raw_data[pos..next],
data_type: ValueType::Undetermined,
validated: false,
start: self.start + pos,
end: self.start + next,
});
pos = utils::skip_spaces(buf, next);
if (pos < len) && ((buf[pos] == b'+') || (buf[pos] == b',') || (buf[pos] == b'|') || (buf[pos] == b';')) {
pos += 1;
}
}
self.data_type = ValueType::List(v);
Ok(())
}
fn validate_alignment(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_alignment().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting an alignment constant (left,topleft,top,topright,right,bottomright,bottom,bottomleft,center) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_dock(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_dock().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting an dock constant (left,top,right,bottom or fill) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_color(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_color().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting an color name (Black,DarkBlue,DarkGreen,Teal,DarkRed,Magenta,Olive,Silver,Gray,Blue,Green,Aqua,Red,Pink,Yellow,White,Transparent) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_size(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_size().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a size (width x height) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_dict(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_dict().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a dictionary with values `{{..}}` for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_list(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_list().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a list with values `[]..]` for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_i32(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_i32().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a numerical value (integer) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_percentage(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_percentage().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a percentage value (a number followed by the percentage % sign) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_dimension(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_dimension().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a value that express a dimension (a positive number followed by the percentage % sign or an absolute value: ex: 50% or 100) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
fn validate_coordonate(&mut self, display_param_name: &str, param_list: &str) -> Result<(), Error> {
if self.get_coordonate().is_some() {
return Ok(());
}
Err(Error::new(
param_list,
format!(
"Expecting a value that express a coordonate (a number followed by the percentage % sign or an absolute value: ex: 50% or -25% or 100 or -2) for parameter '{}' but found '{}'",
display_param_name, self.raw_data
)
.as_str(),
self.start,
self.end,
))
}
pub(crate) fn validate(&mut self, param_list: &str, key_name: &str, expected_type: super::signature::ParamType) -> Result<(), Error> {
let display_param_name = if !self.param_name.is_empty() { self.param_name } else { key_name };
match expected_type {
super::ParamType::String => { }
super::ParamType::Bool => self.validate_bool(display_param_name, param_list)?,
super::ParamType::Flags => self.validate_flags(display_param_name, param_list)?,
super::ParamType::Alignment => self.validate_alignment(display_param_name, param_list)?,
super::ParamType::Dock => self.validate_dock(display_param_name, param_list)?,
super::ParamType::Color => self.validate_color(display_param_name, param_list)?,
super::ParamType::Layout => self.validate_layout(display_param_name, param_list)?,
super::ParamType::LayoutSize => self.validate_layout_size(display_param_name, param_list)?,
super::ParamType::Size => self.validate_size(display_param_name, param_list)?,
super::ParamType::Dict => self.validate_dict(display_param_name, param_list)?,
super::ParamType::List => self.validate_list(display_param_name, param_list)?,
super::ParamType::Integer => self.validate_i32(display_param_name, param_list)?,
super::ParamType::Percentage => self.validate_percentage(display_param_name, param_list)?,
super::ParamType::Dimension => self.validate_dimension(display_param_name, param_list)?,
super::ParamType::Coordonate => self.validate_coordonate(display_param_name, param_list)?,
}
self.validated = true;
Ok(())
}
}