diff --git a/.run/Тест ActionState.run.xml b/.run/Тест ActionState.run.xml
new file mode 100644
index 0000000..161553e
--- /dev/null
+++ b/.run/Тест ActionState.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Тест VersionInfo.run.xml b/.run/Тест VersionInfo.run.xml
new file mode 100644
index 0000000..52d0a0a
--- /dev/null
+++ b/.run/Тест VersionInfo.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/anb_python_components/classes/action_state.py b/anb_python_components/classes/action_state.py
new file mode 100644
index 0000000..90188a3
--- /dev/null
+++ b/anb_python_components/classes/action_state.py
@@ -0,0 +1,210 @@
+# anb_python_components/classes/action_state.py
+from typing import Callable
+
+from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
+from anb_python_components.models.action_state_message import ActionStateMessage, MessageType
+
+class ActionState[T]:
+ """
+ Класс для хранения состояния действия.
+ """
+
+ def __init__ (self, default: T | None = None):
+ self.__messages: list[ActionStateMessage] = []
+ self.value: T | None = default
+
+ @staticmethod
+ def get_string_error_only () -> Callable[[ActionStateMessage], bool]:
+ """
+ Возвращает лямбду для фильтрации только сообщений типа ERROR.
+ """
+ return lambda message: message.message_type == MessageType.ERROR
+
+ @staticmethod
+ def get_string_error_and_warning () -> Callable[[ActionStateMessage], bool]:
+ """
+ Возвращает лямбду для фильтрации сообщений типа ERROR и WARNING.
+ """
+ return lambda message: message.message_type in [MessageType.ERROR, MessageType.WARNING]
+
+ @staticmethod
+ def get_string_all () -> Callable[[ActionStateMessage], bool]:
+ """
+ Возвращает лямбду для фильтрации всех сообщений.
+ """
+ return lambda message: True
+
+ def add_message (self, message: ActionStateMessage):
+ """
+ Добавляет сообщение в список.
+ :param message: Сообщение.
+ """
+ if message:
+ self.__messages.append(message)
+ else:
+ raise ValueError("Сообщение не может быть пустым.")
+
+ def add (self, message_type: MessageType, message: str, flags: dict[str, bool] | None = None):
+ """
+ Добавляет сообщение в список.
+ :param message_type: Тип сообщения.
+ :param message: Сообщение.
+ :param flags: Флаги (необязательный).
+ """
+ # Создаём сообщение
+ message = ActionStateMessage(message_type, message, flags)
+
+ # Добавляем в список
+ self.add_message(message)
+
+ def add_info (self, message: str, flags: dict[str, bool] | None = None):
+ """
+ Добавляет информационное сообщение в список.
+ :param message: Сообщение.
+ :param flags: Флаги (необязательный).
+ """
+ self.add(MessageType.INFO, message, flags)
+
+ def add_warning (self, message: str, flags: dict[str, bool] | None = None):
+ """
+ Добавляет предупреждающее сообщение в список.
+ :param message: Сообщение.
+ :param flags: Флаги (необязательный).
+ """
+ self.add(MessageType.WARNING, message, flags)
+
+ def add_error (self, message: str, flags: dict[str, bool] | None = None):
+ """
+ Добавляет сообщение об ошибке в список.
+ :param message: Сообщение.
+ :param flags: Флаги (необязательный).
+ """
+ self.add(MessageType.ERROR, message, flags)
+
+ def add_state (self, state, clear_all_before: bool = False):
+ """
+ Добавляет другое состояние действия в текущее.
+ :param state:ActionState - Состояние действия.
+ :param clear_all_before - Очистить список перед добавлением (по умолчанию - False).
+
+ ВНИМАНИЕ! Метод не передаёт значение value состояния, а просто переносит его сообщения.
+ """
+ # Проверяем тип
+ if not isinstance(state, ActionState):
+ # - и если не ActionState, то бросаем исключение
+ raise WrongTypeException("Неверный тип состояния действия.", "ActionState", str(type(state)), "state")
+
+ # Если нужно очистить список перед добавлением
+ if clear_all_before:
+ # - то очищаем его
+ self.__messages.clear()
+
+ # Получаем список сообщений из состояния
+ state_messages = state.get_messages()
+
+ # Перебираем все сообщения переданного состояния
+ for state_message in state_messages:
+ # - и если это сообщение состояния
+ if isinstance(state_message, ActionStateMessage):
+ # -- то добавляем его в текущий список
+ self.__messages.append(state_message)
+
+ def get_messages (self, predicate: Callable[[ActionStateMessage], bool] | None = None) -> list[ActionStateMessage]:
+ """
+ Возвращает список сообщений с учетом условия (если указан).
+ :param predicate: Условие выборки.
+ :return: Список сообщений.
+ """
+ # Если условие указано
+ if predicate:
+ # - то фильтруем список по нему
+ return list(filter(predicate, self.__messages))
+ else:
+ # - если нет, то просто возвращаем весь список
+ return self.__messages.copy()
+
+ def get_string_messages (
+ self, predicate: Callable[[ActionStateMessage], bool] | None = None, separator: str = "\n"
+ ) -> str:
+ """
+ Возвращает строку сообщений с учетом условия (если указано).
+ :param predicate: Условие выборки (необязательный).
+ :param separator: Разделитель строк (необязательный, по умолчанию - "\n").
+ :return: Строка сообщений.
+ """
+ # Получаем список сообщений с учетом условия
+ messages = self.get_messages(predicate)
+
+ # Объединяем их в строку и возвращаем
+ return separator.join(map(lambda message: message.message, messages))
+
+ def has_infos (self) -> bool:
+ """
+ Проверяет, есть ли в списке информационные сообщения.
+ :return: True, если есть, иначе False.
+ """
+ return any(message.message_type == MessageType.INFO for message in self.__messages)
+
+ def has_warnings (self) -> bool:
+ """
+ Проверяет, есть ли в списке предупреждающие сообщения.
+ :return: True, если есть, иначе False.
+ """
+ return any(message.message_type == MessageType.WARNING for message in self.__messages)
+
+ def has_errors (self) -> bool:
+ """
+ Проверяет, есть ли в списке сообщения об ошибках.
+ :return: True, если есть, иначе False.
+ """
+ return any(message.message_type == MessageType.ERROR for message in self.__messages)
+
+ def is_success (self, ignore_warnings: bool = False) -> bool:
+ """
+ Проверяет, успешное ли состояние действия.
+ :param ignore_warnings: Игнорировать предупреждения (по умолчанию - False).
+ :return: Успешно ли состояние действия.
+ """
+ # Задаем начальное значение результата
+ result = True
+
+ # Если не нужно игнорировать предупреждения
+ if not ignore_warnings:
+ # - то проверяем наличие предупреждений
+ result = result and not self.has_warnings()
+
+ # Проверяем наличие ошибок
+ result = result and not self.has_errors()
+
+ # Выдаём результат
+ return result
+
+ def clear (self, predicate: Callable[[ActionStateMessage], bool] | None = None):
+ """
+ Очищает список сообщений с учетом условия (если указан).
+ :param predicate: Функция для фильтрации сообщений для очистки.
+ """
+ # Если условие указано
+ if predicate:
+ # - то фильтруем список
+ self.__messages = list(filter(lambda message: not predicate(message), self.__messages))
+ else:
+ # - если нет, то просто очищаем список
+ self.__messages.clear()
+
+ def count (self, predicate: Callable[[ActionStateMessage], bool] | None = None) -> int:
+ """
+ Возвращает количество сообщений в списке с учетом условия (если указано).
+ :param predicate: Условие выборки (необязательный).
+ :return: Количество сообщений.
+ """
+ # Если условие указано
+ if predicate:
+ # - то фильтруем список по нему
+ messages = self.get_messages(predicate)
+
+ # - и возвращаем его длину
+ return len(messages)
+ else:
+ # - если нет, то просто возвращаем длину списка
+ return len(self.__messages)
\ No newline at end of file
diff --git a/anb_python_components/enums/message_type.py b/anb_python_components/enums/message_type.py
new file mode 100644
index 0000000..4cf00f3
--- /dev/null
+++ b/anb_python_components/enums/message_type.py
@@ -0,0 +1,44 @@
+# anb_python_components/enums/message_type.py
+
+from enum import Enum
+
+class MessageType(Enum):
+ """
+ Перечисление типов сообщений:
+ - SUCCESS: Успешное выполнение.
+ - INFO: Информация.
+ - WARNING: Предупреждение.
+ - ERROR: Ошибка.
+ """
+ # Успешное выполнение
+ SUCCESS = 0
+
+ # Информация
+ INFO = 1
+
+ # Предупреждение
+ WARNING = 2
+
+ # Ошибка
+ ERROR = 3
+
+ def __str__ (self):
+ """
+ Переопределение метода __str__.
+ :return: Текстовое представление перечисления.
+ """
+ # Получаем текстовое представление
+ match self:
+ case MessageType.SUCCESS:
+ result = "Успех"
+ case MessageType.INFO:
+ result = "Информация"
+ case MessageType.WARNING:
+ result = "Предупреждение"
+ case MessageType.ERROR:
+ result = "Ошибка"
+ case _:
+ result = "Неизвестный тип сообщения"
+
+ # Возвращаем результат
+ return result
\ No newline at end of file
diff --git a/anb_python_components/models/__init__.py b/anb_python_components/models/__init__.py
new file mode 100644
index 0000000..193583a
--- /dev/null
+++ b/anb_python_components/models/__init__.py
@@ -0,0 +1 @@
+# anb_python_components/models/__init__.py
\ No newline at end of file
diff --git a/anb_python_components/models/action_state_message.py b/anb_python_components/models/action_state_message.py
new file mode 100644
index 0000000..95caaa8
--- /dev/null
+++ b/anb_python_components/models/action_state_message.py
@@ -0,0 +1,27 @@
+# anb_python_components/models/action_state_message.py
+from anb_python_components.enums.message_type import MessageType
+
+class ActionStateMessage:
+ """
+ Модель сообщения о состояния действия.
+ """
+
+ def __init__ (
+ self, message_type: MessageType = MessageType.INFO, message: str = "", flags: dict[str, bool] | None = None
+ ):
+ """
+ Конструктор.
+ :param message_type: Тип сообщения (по умолчанию, INFO).
+ :param message: Текст сообщения (по умолчанию, пустая строка).
+ :param flags: Флаги сообщения (по умолчанию, пустой словарь).
+ """
+ self.message_type: MessageType = message_type
+ self.message: str = message
+ self.flags: dict[str, bool] = flags if flags is not None else []
+
+ def __str__ (self):
+ """
+ Переопределение метода __str__.
+ :return: Текстовое представление модели.
+ """
+ return f"[{str(self.message_type).upper()}] {self.message}"
\ No newline at end of file
diff --git a/anb_python_components/types/two_dim_size.py b/anb_python_components/types/two_dim_size.py
new file mode 100644
index 0000000..0da07b3
--- /dev/null
+++ b/anb_python_components/types/two_dim_size.py
@@ -0,0 +1,413 @@
+# anb_python_components/types/two_dim_size.py
+
+class TwoDimSize:
+ """
+ Класс TwoDimSize для представления размеров двухмерного объекта.
+ """
+
+ # Разделитель частей по умолчанию.
+ DEFAULT_DELIMITER: str = ':'
+
+ @staticmethod
+ def _get_valid_num (num: int, min_num: int | None = 0, max_num: int | None = None) -> int:
+ """
+ Получает корректное значение представленного числа.
+ :param num: Проверяемое значение.
+ :param min_num: Минимальное значение (по умолчанию 0). Если None, то считается, что минимальное значение не ограничено.
+ :param max_num: Максимальное значение (по умолчанию None). Если None, то считается, что максимальное значение не ограничено.
+ :return: Корректное значение.
+ """
+ # Флаг, разрешающий или запрещающий отрицательные значения
+ is_negative_allowed = min_num < 0 or min_num is None
+
+ # Безлимитные значения
+ # - для минимального значения
+ is_unlimited_min = min_num is None
+ # - для максимального значения
+ is_unlimited_max = max_num is None
+
+ # Если значение отрицательное, а отрицательные значения запрещены
+ if num < 0 and not is_negative_allowed:
+ # - то возвращаю 0
+ return 0
+
+ # Если значение вышло за ограничения минимальное значения
+ if not is_unlimited_min and num < min_num:
+ # - то возвращаю минимальное значение
+ return min_num
+
+ # Если значение вышло за ограничения максимальное значения
+ if not is_unlimited_max and num > max_num:
+ # - то возвращаю максимальное значение
+ return max_num
+
+ # Если значение корректно, то возвращаю его
+ return num
+
+ def __init__ (
+ self, width: int = 0, height: int = 0, min_width: int | None = 0, max_width: int | None = None,
+ min_height: int | None = 0, max_height: int | None = None
+ ):
+ """
+ Инициализирует объект TwoDimSize.
+ :param width: Ширина (по умолчанию 0).
+ :param height: Высота (по умолчанию 0).
+ :param min_width: Минимальная ширина (по умолчанию 0). Если None, то считается, что минимальная ширина не ограничена.
+ :param max_width: Максимальная ширина (по умолчанию None). Если None, то считается, что максимальная ширина не ограничена.
+ :param min_height: Минимальная высота (по умолчанию 0). Если None, то считается, что минимальная высота не ограничена.
+ :param max_height: Максимальная высота (по умолчанию None). Если None, то считается, что максимальная высота не ограничена.
+ """
+
+ # Если некорректно заданы минимальные и максимальные значения ширины
+ if min_width > max_width:
+ # - то выбрасываю исключение
+ raise ValueError("Минимальная ширина не может быть больше максимальной.")
+
+ # Если некорректно заданы минимальные и максимальные значения высоты
+ elif min_height > max_height:
+ # - то выбрасываю исключение
+ raise ValueError("Минимальная высота не может быть больше максимальной.")
+
+ # Задаю минимальные и максимальные
+ # - для ширины
+ # -- минимальное
+ self._min_width: int | None = min_width
+ # -- максимальное
+ self._max_width: int | None = max_width
+ # - для высоты
+ # -- минимальное
+ self._min_height: int | None = min_height
+ # -- максимальное
+ self._max_height: int | None = max_height
+
+ # Устанавливаю ширину
+ self._width: int = self._get_valid_num(width, min_width, max_width)
+
+ # Устанавливаю высоту
+ self._height: int = self._get_valid_num(height, min_height, max_height)
+
+ @property
+ def min_width (self) -> int:
+ """
+ Минимальная ширина.
+ :return: Значение минимальной ширины.
+ """
+ return self._min_width
+
+ @min_width.setter
+ def min_width (self, value: int):
+ """
+ Устанавливает минимальную ширину и пересчитывает ширину, если она выходит за новые ограничения.
+ :param value: Новое значение минимальной ширины.
+ :return: None
+ """
+ # Если максимальная ширина ограничена и значение минимальной ширины больше максимальной ширины,
+ # то устанавливаю минимальную ширину равной максимальной ширине, иначе возвращаю значение
+ self._min_width = self._max_width if self._max_width is not None and value > self._max_width else value
+
+ # При изменении минимальной ширины пересчитываю ширину
+ self.width = self._width
+
+ @min_width.deleter
+ def min_width (self):
+ """
+ Удаляет минимальную ширину и устанавливает минимальную ширину в 0.
+ :return: None
+ """
+ self._min_width = 0
+
+ @property
+ def max_width (self) -> int:
+ """
+ Максимальная ширина.
+ :return: Значение максимальной ширины.
+ """
+ return self._max_width
+
+ @max_width.setter
+ def max_width (self, value: int):
+ """
+ Устанавливает максимальную ширину и пересчитывает ширину, если она выходит за новые ограничения.
+ :param value: Новое значение максимальной ширины.
+ :return: None
+ """
+ # Если минимальная ширина ограничена и значение максимальной ширины меньше минимальной ширины,
+ # то устанавливаю максимальную ширину равной минимальной ширине, иначе возвращаю значение
+ self._max_width = self._min_width if self._min_width is not None and value < self._min_width else value
+
+ # При изменении максимальной ширины пересчитываю ширину
+ self.width = self._width
+
+ @max_width.deleter
+ def max_width (self):
+ """
+ Удаляет максимальную ширину.
+ :return: None
+ """
+ self._max_width = None
+
+ @property
+ def min_height (self) -> int:
+ """
+ Минимальная высота.
+ :return: Значение минимальной высоты.
+ """
+ return self._min_height
+
+ @min_height.setter
+ def min_height (self, value: int):
+ """
+ Устанавливает минимальную высоту и пересчитывает высоту, если она выходит за новые ограничения.
+ :param value: Новое значение минимальной высоты.
+ :return: None
+ """
+ # Если максимальная высота ограничена и значение минимальной высоты больше максимальной высоты,
+ # то устанавливаю минимальную высоту равной максимальной высоте, иначе возвращаю значение
+ self._min_height = self._max_height if self._max_height is not None and value > self._max_height else value
+
+ # При изменении минимальной высоты пересчитываю высоту
+ self.height = self._height
+
+ @min_height.deleter
+ def min_height (self):
+ """
+ Удаляет минимальную высоту и устанавливает минимальную высоту в 0.
+ :return: None
+ """
+ self._min_height = 0
+
+ @property
+ def max_height (self) -> int:
+ """
+ Максимальная высота.
+ :return: Значение максимальной высоты.
+ """
+ return self._max_height
+
+ @max_height.setter
+ def max_height (self, value: int):
+ """
+ Устанавливает максимальную высоту и пересчитывает высоту, если она выходит за новые ограничения.
+ :param value: Новое значение максимальной высоты.
+ :return: None
+ """
+ # Если минимальная высота ограничена и значение максимальной высоты меньше минимальной высоты,
+ # то устанавливаю максимальную высоту равной минимальной высоте, иначе возвращаю значение
+ self._max_height = self._min_height if self._min_height is not None and value < self._min_height else value
+
+ # При изменении максимальной высоты пересчитываю высоту
+ self.height = self._height
+
+ @max_height.deleter
+ def max_height (self):
+ """
+ Удаляет максимальную высоту.
+ :return: None
+ """
+ self._max_height = None
+
+ @property
+ def width (self) -> int:
+ """
+ Ширина.
+ :return: Установленная ширина.
+ """
+ return self._width
+
+ @width.setter
+ def width (self, value: int):
+ """
+ Устанавливает ширину.
+ :param value: Новое значение ширины.
+ :return: None
+ """
+ self._width = self._get_valid_num(value, self.min_width, self.max_width)
+
+ @width.deleter
+ def width (self):
+ """
+ Удаляет ширину.
+ :return: None
+ """
+ self._width = 0 if self._min_width is None else self._min_width
+
+ @property
+ def height (self) -> int:
+ """
+ Высота.
+ :return: Установленная высота.
+ """
+ return self._height
+
+ @height.setter
+ def height (self, value: int):
+ """
+ Устанавливает высоту.
+ :param value: Новое значение высоты.
+ :return: None
+ """
+ self._height = self._get_valid_num(value, self.min_height, self.max_height)
+
+ @height.deleter
+ def height (self):
+ """
+ Удаляет высоту.
+ :return: None
+ """
+ self._height = 0 if self._min_height is None else self._min_height
+
+ def as_str (self, delimiter: str = DEFAULT_DELIMITER) -> str:
+ """
+ Возвращает строковое представление объекта TwoDimSize в формате "ширина:высота".
+ :param delimiter: Разделитель частей ширины и высоты (по умолчанию ":").
+ :return: Строковое представление объекта TwoDimSize.
+ """
+ return f"{self.width}{delimiter}{self.height}"
+
+ def __str__ (self) -> str:
+ """
+ Строковое представление объекта TwoDimSize.
+ :return: Строковое представление объекта TwoDimSize.
+ """
+ return self.as_str(TwoDimSize.DEFAULT_DELIMITER)
+
+ def as_tuple (self) -> tuple[int, int]:
+ """
+ Возвращает кортеж (ширина, высота).
+ :return: Кортеж (ширина, высота).
+ """
+ return self.width, self.height
+
+ def __eq__ (self, other: object) -> bool:
+ """
+ Сравнивает объект TwoDimSize с другим объектом.
+ :param other: Объект для сравнения.
+ :return: True, если объекты равны, иначе False.
+ """
+ # Если сравниваем с объектом TwoDimSize
+ if isinstance(other, TwoDimSize):
+ # - то сравниваю ширину и высоту
+ return self.width == other.width and self.height == other.height
+ else:
+ # - иначе возвращаю False
+ return False
+
+ def __repr__ (self) -> str:
+ """
+ Строковое представление объекта TwoDimSize для отладки.
+ :return: Строковое представление объекта TwoDimSize для отладки.
+ """
+ return f"TwoDimSize({self.width}, {self.height}, min_width={self.min_width}, max_width={self.max_width}, min_height={self.min_height}, max_height={self.max_height})"
+
+ def __hash__ (self) -> int:
+ """
+ Хэш объекта TwoDimSize.
+ :return: Хэш объекта TwoDimSize.
+ """
+ return hash((self.width, self.height))
+
+ def __copy__ (self) -> 'TwoDimSize':
+ """
+ Копирует объект TwoDimSize.
+ :return: Копия объекта TwoDimSize.
+ """
+ return TwoDimSize(self.width, self.height, self.min_width, self.max_width, self.min_height, self.max_height)
+
+ def __deepcopy__ (self, memo: dict) -> 'TwoDimSize':
+ """
+ Глубокое копирование объекта TwoDimSize.
+ :param memo: Словарь для хранения копий объектов.
+ :return: Глубокая копия объекта TwoDimSize.
+ """
+ # Если объект уже был скопирован
+ if id(self) in memo:
+ # - то возвращаю его копию
+ return memo[id(self)]
+ else:
+ # - иначе создаю копию объекта
+ memo[id(self)] = copy = TwoDimSize(
+ self.width, self.height, self.min_width, self.max_width, self.min_height, self.max_height
+ )
+ # - и возвращаю её
+ return copy
+
+ def __add__ (self, other: 'TwoDimSize') -> 'TwoDimSize':
+ """
+ Складывает объект TwoDimSize с другим объектом TwoDimSize.
+ :param other: Объект TwoDimSize для сложения.
+ :return: Объект TwoDimSize, полученный после сложения.
+ """
+ # Если другой объект является объектом TwoDimSize
+ if isinstance(other, TwoDimSize):
+ # - то складываю ширину и высоту с объектом TwoDimSize и возвращаю результат в виде объекта TwoDimSize
+ return TwoDimSize(
+ self.width + other.width, self.height + other.height, self.min_width, self.max_width,
+ self.min_height, self.max_height
+ )
+ else:
+ # - иначе выбрасываю исключение
+ raise TypeError("Невозможно сложить два объекта разных типов.")
+
+ def __mul__ (self, other: int) -> 'TwoDimSize':
+ """
+ Умножает объект TwoDimSize на целое число.
+ :param other: Целое число для умножения.
+ :return: Объект TwoDimSize, полученный после умножения.
+ """
+ # Если другой объект является целым числом
+ if isinstance(other, int):
+ # - то перемножаю ширину и высоту на целое число и возвращаю результат в виде объекта TwoDimSize
+ return TwoDimSize(
+ self.width * other, self.height * other, self.min_width, self.max_width, self.min_height,
+ self.max_height
+ )
+ else:
+ # - иначе выбрасываю исключение
+ raise TypeError("Невозможно перемножить объект TwoDimSize на другой объект.")
+
+ @staticmethod
+ def parse (
+ text: str, delimiter: str = DEFAULT_DELIMITER, min_width: int | None = 0, max_width: int | None = None,
+ min_height: int | None = 0, max_height: int | None = None
+ ) -> 'TwoDimSize':
+ """
+ Создает объект TwoDimSize из строки.
+ :param max_height: Минимальная высота (по умолчанию 0). Если None, то считается, что минимальная высота не ограничена.
+ :param min_height: Максимальная высота (по умолчанию None). Если None, то считается, что максимальная высота не ограничена.
+ :param max_width: Максимальная ширина (по умолчанию None). Если None, то считается, что максимальная ширина не ограничена.
+ :param min_width: Минимальная ширина (по умолчанию 0). Если None, то считается, что минимальная ширина не ограничена.
+ :param text: Строка для создания объекта TwoDimSize.
+ :param delimiter: Разделитель частей ширины и высоты (по умолчанию ":").
+ :return: Объект TwoDimSize.
+ :raises ValueError: Если строка имеет неверный формат.
+ :raises TypeError: При попытке преобразовать строку в объект int (ширину или высоту).
+ """
+ # Разделяю значения
+ split_sizes = text.split(delimiter)
+
+ # Проверяю, что массив имеет ровно два элемента
+ if len(split_sizes) != 2:
+ # - иначе выбрасываю исключение
+ raise ValueError("Неверный формат строки для TwoDimSize.")
+
+ # Получаю ширину и высоту
+ width, height = split_sizes
+
+ # Проверяю, что ширина получена
+ if width:
+ # - и перевожу ее в целое число
+ width = int(width)
+ else:
+ # - иначе выбрасываю исключение
+ raise ValueError("Неверный формат ширины в строке для TwoDimSize.")
+
+ # Проверяю, что высота получена
+ if height:
+ # - и перевожу ее в целое число
+ height = int(height)
+ else:
+ # - иначе выбрасываю исключение
+ raise ValueError("Неверный формат высоты в строке для TwoDimSize.")
+
+ # Если все проверки пройдены успешно, то создаю объект TwoDimSize с заданными шириной и высотой и возвращаю его
+ return TwoDimSize(width, height, min_width, max_width, min_height, max_height)
\ No newline at end of file
diff --git a/anb_python_components/types/version_info.py b/anb_python_components/types/version_info.py
new file mode 100644
index 0000000..f5b6237
--- /dev/null
+++ b/anb_python_components/types/version_info.py
@@ -0,0 +1,461 @@
+# anb_python_components/types/version_info.py
+import re
+
+from anb_python_components.extensions.string_extension import StringExtension
+
+class VersionInfo:
+ """
+ Класс для работы с версиями.
+ """
+ # Шаблон вывода по умолчанию.
+ DEFAULT_TEMPLATE: str = '#{Major}.#{Minor}.#{Release}.#{Build} #{Stage} #{StageNumber}'
+
+ def __init__ (self, major: int, minor: int, release: int, build: int, stage: str = '', stage_number: int = 0):
+ """
+ Создание экземпляра класса VersionInfo.
+ :param major: Мажорная версия.
+ :param minor: Минорная версия.
+ :param release: Номер релиза.
+ :param build: Номер сборки.
+ :param stage: Стадия.
+ :param stage_number: Номер стадии.
+ """
+ self.__major: int = major
+ self.__minor: int = minor
+ self.__release: int = release
+ self.__build: int = build
+ self.__stage: str = stage
+ self.__stage_number: int = stage_number
+
+ # Мажорная версия
+ @property
+ def major (self) -> int:
+ """
+ Получение значения major.
+ :return: Значение major.
+ """
+ return self.__major
+
+ @major.setter
+ def major (self, value: int):
+ """
+ Установка значения major.
+ :param value: Значение major.
+ :return: None
+ """
+ self.__major = value if value >= 0 else 0
+
+ @major.deleter
+ def major (self):
+ """
+ Удаление значения major.
+ :return: None
+ """
+ self.__major = 0
+
+ # Минорная версия
+ @property
+ def minor (self) -> int:
+ """
+ Получение значения minor.
+ :return: Значение minor.
+ """
+ return self.__minor
+
+ @minor.setter
+ def minor (self, value: int):
+ """
+ Установка значения minor.
+ :param value: Значение minor.
+ :return: None
+ """
+ self.__minor = value if value >= 0 else 0
+
+ @minor.deleter
+ def minor (self):
+ """
+ Удаление значения minor.
+ :return: None
+ """
+ self.__minor = 0
+
+ # Номер релиза
+ @property
+ def release (self) -> int:
+ """
+ Получение значения release.
+ :return: Значение release.
+ """
+ return self.__release
+
+ @release.setter
+ def release (self, value: int):
+ """
+ Установка значения release.
+ :param value: Значение release.
+ :return: None
+ """
+ self.__release = value if value >= 0 else 0
+
+ @release.deleter
+ def release (self):
+ """
+ Удаление значения release.
+ :return: None
+ """
+ self.__release = 0
+
+ # Номер сборки
+ @property
+ def bild (self) -> int:
+ """
+ Получение значения bild.
+ :return: Значение bild.
+ """
+ return self.__build
+
+ @bild.setter
+ def bild (self, value: int):
+ """
+ Установка значения bild.
+ :param value: Значение bild.
+ :return: None
+ """
+ self.__build = value if value >= 0 else 0
+
+ @bild.deleter
+ def bild (self):
+ """
+ Удаление значения bild.
+ :return: None
+ """
+ self.__build = 0
+
+ # Стадия
+ @property
+ def stage (self) -> str:
+ """
+ Получение значения stage.
+ :return: Значение stage.
+ """
+ return self.__stage
+
+ @stage.setter
+ def stage (self, value: str | None):
+ """
+ Установка значения stage.
+ :param value: Значение stage.
+ :return: None
+ """
+ self.__stage = value if value is not None else ''
+
+ @stage.deleter
+ def stage (self):
+ """
+ Удаление значения stage.
+ :return: None
+ """
+ self.__stage = ''
+
+ # Номер стадии
+ @property
+ def stage_number (self) -> int:
+ """
+ Получение значения stage_number.
+ :return: Значение stage_number.
+ """
+ return self.__stage_number
+
+ @stage_number.setter
+ def stage_number (self, value: int):
+ """
+ Установка значения stage_number.
+ :param value: Значение stage_number.
+ :return: None
+ """
+ self.__stage_number = value if value >= 0 else 0
+
+ @stage_number.deleter
+ def stage_number (self):
+ """
+ Удаление значения stage_number.
+ :return: None
+ """
+ self.__stage_number = 0
+
+ def to_string (self, template: str = DEFAULT_TEMPLATE) -> str:
+ """
+ Преобразование экземпляра класса VersionInfo в строку.
+ :param template: Шаблон для преобразования.
+ :return: Строка с версией.
+ """
+ # Создание словаря для замены
+ template_dict = {
+ '#{Major}': str(self.major),
+ '#{Minor}': str(self.minor),
+ '#{Release}': str(self.release),
+ '#{Build}': str(self.bild),
+ '#{Stage}': str(self.stage),
+ '#{StageNumber}': str(self.stage_number) if self.stage_number > 0 else ''
+ }
+
+ # Замена значений в шаблоне
+ replaced = StringExtension.replace_all(template_dict, template)
+
+ # Удаление лишних пробелов и символов в начале и конце строки и возврат результата
+ return replaced.strip()
+
+ def __str__ (self) -> str:
+ """
+ Переопределение метода __str__.
+ :return: Строка с версией.
+ """
+ return self.to_string(VersionInfo.DEFAULT_TEMPLATE)
+
+ def __repr__ (self) -> str:
+ """
+ Переопределение метода __repr__.
+ :return: Строка с версией.
+ """
+ return f'VersionInfo(major={self.major}, minor={self.minor}, release={self.release}, build={self.bild}, stage={self.stage}, stage_number={self.stage_number})'
+
+ def __compare (self, other: 'VersionInfo') -> int:
+ """
+ Сравнение версий.
+ :param other: Версия для сравнения.
+ :return: 1, если текущая версия больше, -1, если текущая версия меньше, 0, если версии равны.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Сравнение мажорных версий. Если текущая мажорная версия больше
+ if self.major > other.major:
+ # - возвращаем 1
+ return 1
+ # Если текущая мажорная версия меньше
+ elif self.major < other.major:
+ # - возвращаем -1
+ return -1
+
+ # Если мажорные версии равны, то сравниваем минорные версии. Если текущая минорная версия больше
+ if self.minor > other.minor:
+ # - возвращаем 1
+ return 1
+ # Если текущая минорная версия меньше
+ elif self.minor < other.minor:
+ # - возвращаем -1
+ return -1
+
+ # Если мажорные и минорные версии равны, то сравниваем номер релиза. Если текущий номер релиза больше
+ if self.release > other.release:
+ # - возвращаем 1
+ return 1
+ # Если текущий номер релиза меньше
+ elif self.release < other.release:
+ # - возвращаем -1
+ return -1
+
+ # Если мажорные, минорные и номер релиза равны, то сравниваем номер сборки. Если текущий номер сборки больше
+ if self.bild > other.bild:
+ # - возвращаем 1
+ return 1
+ # Если текущий номер сборки меньше
+ elif self.bild < other.bild:
+ # - возвращаем -1
+ return -1
+
+ # Если мажорные, минорные, номер релиза и номер сборки равны, то равны и версии. Возвращаем 0.
+ return 0
+
+ def __eq__ (self, other: 'VersionInfo') -> bool:
+ """
+ Сравнение версий на равенство (==).
+ :param other: Версия для сравнения.
+ :return: True, если версии равны, False, если версии не равны.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Если версии равны, то возвращаем True
+ return self.__compare(other) == 0
+
+ def __ne__ (self, other: 'VersionInfo') -> bool:
+ """
+ Сравнение версий на неравенство (!=).
+ :param other: Версия для сравнения.
+ :return: True, если версии не равны, False, если версии равны.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Если версии не равны, то возвращаем True
+ return self.__compare(other) != 0
+
+ def __lt__ (self, other: 'VersionInfo') -> bool:
+ """
+ Сравнение версий на меньше (<).
+ :param other: Версия для сравнения.
+ :return: True, если текущая версия меньше, False, если текущая версия больше или равна.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Если текущая версия меньше, то возвращаем True
+ return self.__compare(other) == -1
+
+ def __gt__ (self, other: 'VersionInfo') -> bool:
+ """
+ Сравнение версий на больше (>).
+ :param other: Версия для сравнения.
+ :return: True, если текущая версия больше, False, если текущая версия меньше или равна.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Если текущая версия больше, то возвращаем True
+ return self.__compare(other) == 1
+
+ def __le__ (self, other: 'VersionInfo') -> bool:
+ """
+ Сравнение версий на меньше или равно (<=).
+ :param other: Версия для сравнения.
+ :return: True, если текущая версия меньше или равна, False, если текущая версия больше.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Если текущая версия меньше или равна, то возвращаем True
+ return self.__compare(other) in (0, -1)
+
+ def __ge__ (self, other: 'VersionInfo') -> bool:
+ """
+ Сравнение версий на больше или равно (>=).
+ :param other: Версия для сравнения.
+ :return: True, если текущая версия больше или равна, False, если текущая версия меньше.
+ """
+ # Проверка типа
+ if not isinstance(other, VersionInfo):
+ # - если other не является экземпляром VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}')
+
+ # Если текущая версия больше или равна, то возвращаем True
+ return self.__compare(other) in (0, 1)
+
+ def in_range (
+ self, start: 'VersionInfo' or None = None, end: 'VersionInfo' or None = None, start_inclusive: bool = True,
+ end_inclusive: bool = True
+ ) -> bool:
+ """
+ Проверка версии на принадлежность диапазону.
+ :param start: Начало диапазона (по умолчанию None). Если start равен None, то считается, что start не ограничен.
+ :param end: Конец диапазона (по умолчанию None). Если end равен None, то считается, что end не ограничен.
+ :param start_inclusive: Включать ли начало диапазона (по умолчанию True).
+ :param end_inclusive: Включать ли конец диапазона (по умолчанию True).
+ :return: True, если версия принадлежит диапазону, False, если версия не принадлежит диапазону.
+ """
+ # Если start не указан
+ if start is None:
+ # - устанавливаем start_inclusive равным True
+ start_inclusive = True
+ # - устанавливаем start равным self
+ start = self
+
+ # Если end не указан
+ if end is None:
+ # - устанавливаем end_inclusive равным True
+ end_inclusive = True
+ # - устанавливаем end равным self
+ end = self
+
+ # Проверка типов
+ if not isinstance(start, VersionInfo) or not isinstance(end, VersionInfo):
+ # - если start или end не являются экземплярами VersionInfo, то выбрасываем исключение
+ raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(start)} и {type(end)}')
+
+ # Если start совпадает с версией
+ if self == start:
+ # - если включать начало диапазона (start_inclusive), то возвращаем True, иначе False
+ return True if start_inclusive else False
+
+ # Если end совпадает с версией
+ if self == end:
+ # - если включать конец диапазона (end_inclusive), то возвращаем True, иначе False
+ return True if end_inclusive else False
+
+ # Если текущая версия находится между start и end, то возвращаем True, иначе False
+ return True if start < self < end else False
+
+ @staticmethod
+ def parse (version: str) -> 'VersionInfo':
+ version = version.strip()
+
+ # Разбиваем строку на части по пробелам (1 часть - основная - мажор, минор, релиз, сборка,
+ # 2 часть - стадия и 3 - номер стадии):
+ # - находим позицию первого пробела
+ start = version.find(" ")
+ # - если позиция первого пробела не найдена
+ if start == -1:
+ # - устанавливаем конец строки
+ start = len(version)
+ # - находим позицию последнего пробела
+ end = version.rfind(" ") if start < len(version) else -1
+
+ # - получаем основную часть
+ main_part = version[:start].strip()
+ # - получаем стадию
+ stage = version[start:end].strip() if end > 0 else ''
+ # - получаем номер стадии в виде строки
+ stage_number_text = version[end:].strip()
+ # - получаем номер стадии из строки
+ try:
+ stage_number = int(stage_number_text)
+ except ValueError:
+ stage_number = 0
+
+ # Составляем регулярное выражение для парсинга базовой информации о версии
+ pattern = r'^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?$'
+
+ # Парсим базовую информацию о версии
+ matches = re.match(pattern, main_part)
+
+ # Если не удалось найти соответствие
+ if not matches:
+ # - возвращаем пустую версию
+ return VersionInfo(0, 0, 0, 0, stage, stage_number)
+
+ # Получаем группы из совпадения
+ groups = matches.groups()
+
+ # Проверяем, что найдены как минимум 2 части
+ if len(groups) < 2:
+ # - иначе возвращаем пустую версию
+ return VersionInfo(0, 0, 0, 0, stage, stage_number)
+
+ # Проверяем, что групп 4
+ if len(groups) != 4:
+ # - иначе возвращаем пустую версию
+ return VersionInfo(0, 0, 0, 0, stage, stage_number)
+
+ # Распаковываем значения
+ major, minor, release, build = groups
+
+ # Преобразуем строки в целые числа
+ major = int(major) if major else 0
+ minor = int(minor) if minor else 0
+ release = int(release) if release else 0
+ build = int(build) if build else 0
+
+ # Возвращаем экземпляр класса VersionInfo с полученными значениями
+ return VersionInfo(major, minor, release, build, stage, stage_number)
\ No newline at end of file
diff --git a/help/class_desc/classes/action_state.md b/help/class_desc/classes/action_state.md
new file mode 100644
index 0000000..3d48d04
--- /dev/null
+++ b/help/class_desc/classes/action_state.md
@@ -0,0 +1,379 @@
+# Класс `ActionState`
+
+Класс `ActionState` предназначен для отслеживания состояния выполняемых действий в программе, фиксируя изменения,
+происходящие в процессе выполнения, а также накапливая сообщения о событиях, успехах и ошибках. Он служит контейнером
+для сохранения промежуточных состояний и обеспечивает простой интерфейс взаимодействия с ними.
+
+## Основная информация
+
+- **Имя файла**: anb_python_components\classes\action_state.py
+- **Автор**: Александр Бабаев
+- **Версия**: 1.0.0
+- **Дата начала поддержки**: с версии 1.0
+
+## Атрибуты и методы класса
+
+### Атрибуты
+
+- **`value: T`**: Хранит основное значение состояния (может быть любым объектом).
+- **`__messages: list[ActionStateMessage]`**: Внутренний список сообщений, накопленных в процессе выполнения действия.
+
+### Конструктор (`__init__`)
+
+Принимает необязательное значение по умолчанию, которое устанавливается в качестве основного значения состояния.
+
+**Параметры**:
+
+- `default: T | None = None`: Значение по умолчанию.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+```
+
+### Основные методы
+
+#### Метод `add_message`
+
+Добавляет новое сообщение в список сообщений.
+
+**Параметры**:
+
+- `message: ActionStateMessage`: Модель сообщения.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState, ActionStateMessage, MessageType
+
+state = ActionState[bool](False)
+state.add_message(ActionStateMessage(MessageType.INFO, "Процесс запущен."))
+```
+
+---
+
+#### Метод `add`
+
+Удобный способ добавления сообщений, принимающий непосредственно тип сообщения и его текст.
+
+**Параметры**:
+
+- `message_type: MessageType`: Тип сообщения.
+- `message: str`: Сообщение.
+- `flags: dict[str, bool] | None = None`: Флаги сообщения (необязательны).
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState, MessageType
+
+state = ActionState[bool](False)
+state.add(MessageType.WARNING, "Скорость низкая.")
+```
+
+---
+
+### Метод `add_info`
+
+Добавляет информационное сообщение в список состояний.
+
+**Параметры**:
+
+- `message: str`: Текст самого информационного сообщения.
+- `flags: dict[str, bool] | None = None`: Дополнительные мета-данные или признаки сообщения (например, отметка о
+ важности или срочности).
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+state.add_info("Начали загрузку данных.")
+```
+
+---
+
+### Метод `add_warning`
+
+Добавляет предупреждение в список состояний.
+
+**Параметры**:
+
+- `message: str`: Текст предупреждения.
+- `flags: dict[str, bool] | None = None`: Дополнительные флаги для конкретного предупреждения.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+state.add_warning("Не хватает оперативной памяти.")
+```
+
+---
+
+### Метод `add_error`
+
+Добавляет сообщение об ошибке в список состояний.
+
+**Параметры**:
+
+- `message: str`: Текст сообщения об ошибке.
+- `flags: dict[str, bool] | None = None`: Дополнительные флаги для конкретной ошибки.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+state.add_error("Не удалось установить соединение с базой данных.")
+```
+
+---
+
+#### Метод `add_state`
+
+Объединяет текущее состояние с другим состоянием, перенося его сообщения.
+
+**Параметры**:
+
+- `state: ActionState`: Другое сообщение о состоянии.
+- `clear_all_before: bool = False`: Очистить список перед добавлением (по умолчанию - False).
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+another_state = ActionState()
+another_state.add_info("Дополнительная информация.")
+state.add_state(another_state)
+```
+
+---
+
+#### Метод `get_messages`
+
+Возвращает список сообщений, возможно применяя фильтр по условию.
+
+**Параметры**:
+
+- `predicate: Callable[[ActionStateMessage], bool] | None = None`: Условие выборки.
+
+**Метод возвращает** `list[ActionStateMessage]`: список сообщений.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState, MessageType
+
+state = ActionState[bool](False)
+errors = state.get_messages(lambda m: m.message_type == MessageType.ERROR)
+```
+
+---
+
+### Метод `get_string_messages`
+
+Возвращает объединённую строку сообщений, фильтруя их по определённому критерию (предикат). Полезен для визуализации
+сообщений в логах или отчётах.
+
+**Параметры**:
+
+- `predicate: Callable[[ActionStateMessage], bool] | None = None`: Функциональный предикат для фильтрации сообщений
+ (например, показывать только ошибки или предупреждения).
+- `separator: str = "\n"`: Разделитель между сообщениями (по умолчанию новая строка).
+
+**Метод возвращает** `str`: строка сообщений.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState, MessageType
+
+state = ActionState[bool](False)
+state.add_error("Ошибка в сети.")
+state.add_warning("Медленная скорость загрузки.")
+
+filtered_messages = state.get_string_messages(predicate = lambda msg: msg.message_type == MessageType.ERROR)
+print(filtered_messages) # "Ошибка в сети."
+```
+
+---
+
+### Метод `has_infos`
+
+Проверяет, имеются ли в списке сообщения информационного типа (`INFO`).
+
+**Метод возвращает** `bool`: True, если есть, иначе False.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+state.add_info("Процесс завершён.")
+
+if state.has_infos():
+ print("Имеются информационные сообщения.") # Будет выведено
+```
+
+---
+
+### Метод `has_warnings`
+
+Проверяет, присутствуют ли в списке предупреждающие сообщения (`WARNING`).
+
+**Метод возвращает** `bool`: True, если есть, иначе False.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+state.add_warning("Недостаточно памяти.")
+
+if state.has_warnings():
+ print("Есть предупреждающие сообщения.") # Будет выведено
+```
+
+---
+
+### Метод `has_errors`
+
+Проверяет, существуют ли в списке сообщения об ошибках (`ERROR`).
+
+**Метод возвращает** `bool`: True, если есть, иначе False.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+state.add_error("Время ожидания истекло.")
+
+if state.has_errors():
+ print("Присутствуют сообщения об ошибках.") # Будет выведено
+```
+
+---
+
+### Метод `clear`
+
+Очищает список сообщений, возможно применяя условие фильтрации.
+
+**Параметры**:
+
+- `predicate: Callable[[ActionStateMessage], bool] | None = None`: Функциональный предикат для фильтрации сообщений.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState, MessageType
+
+state = ActionState[bool](False)
+state.clear(lambda m: m.message_type == MessageType.INFO)
+```
+
+---
+
+### Метод `is_success`
+
+Проверяет, было ли состояние успешным, основываясь на наличии ошибок и предупреждений.
+
+**Параметры**:
+
+- `ignore_warnings: bool = False`: Игнорировать предупреждения (по умолчанию - False).
+
+**Метод возвращает** `bool`: True, если успешно, иначе False.
+
+**Пример использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+if state.is_success():
+ print("Операция прошла успешно.")
+```
+
+---
+
+### Метод `count`
+
+Метод `count` позволяет получать количество сообщений в списке, потенциально применяя дополнительное условие
+фильтрации (предикат). Таким образом, можно точно подсчитать число нужных сообщений (например, ошибок, предупреждений
+или общих записей).
+
+**Параметры**:
+
+- **`predicate: Callable[[ActionStateMessage], bool] | None = None`**: Опциональный предикат-функция, которая получает
+ каждое сообщение и решает, включать ли его в итоговый счётчик. Если предикат не указан, учитываются все сообщения.
+
+**Метод возвращает** `int`: количество сообщений.
+
+**Пример использования**:
+
+Предположим, мы хотим посчитать количество сообщений каждого типа:
+
+```python
+from anb_python_components.classes.action_state import ActionState, MessageType
+
+state = ActionState[bool](False)
+state.add_info("Данные получены.")
+state.add_warning("Память почти заполнена.")
+state.add_error("Не удаётся подключиться к серверу.")
+
+# Посчитать общее количество сообщений
+total_messages = state.count()
+print(total_messages) # Вывод: 3
+
+# Посчитать количество ошибок
+error_count = state.count(predicate = lambda msg: msg.message_type == MessageType.ERROR)
+print(error_count) # Вывод: 1
+
+# Посчитать количество информационных сообщений
+info_count = state.count(predicate = lambda msg: msg.message_type == MessageType.INFO)
+print(info_count) # Вывод: 1
+```
+
+### Вспомогательные статичные методы для фильтрации сообщений
+
+Эти методы предоставляют готовые предикаты для извлечения конкретных типов сообщений.
+
+- `get_string_error_only() -> Callable[[ActionStateMessage], bool]`: Возвращает предикат для фильтрации только сообщений
+ об ошибках.
+- `get_string_error_and_warning() -> Callable[[ActionStateMessage], bool]`: Возвращает предикат для фильтрации сообщений
+ об ошибках и предупреждениях.
+- `get_string_all() -> Callable[[ActionStateMessage], bool]`: Возвращает предикат, включающий все сообщения (без
+ фильтрации).
+
+**Примеры использования**:
+
+```python
+from anb_python_components.classes.action_state import ActionState
+
+state = ActionState[bool](False)
+# Получить только ошибки
+only_errors = state.get_string_messages(predicate = ActionState.get_string_error_only(), separator = ", ")
+
+# Все сообщения
+all_messages = state.get_string_messages(predicate = ActionState.get_string_all())
+```
+
+## Заключение
+
+Класс `ActionState` играет ключевую роль в мониторинге и управлении состояниями в приложениях, позволяя контролировать
+ход выполнения операций и оперативно реагировать на возникающие события и ошибки.
+
+[На главную](../../index.md)
\ No newline at end of file
diff --git a/help/class_desc/enums/message_type.md b/help/class_desc/enums/message_type.md
new file mode 100644
index 0000000..388ac8a
--- /dev/null
+++ b/help/class_desc/enums/message_type.md
@@ -0,0 +1,26 @@
+# Перечисление `MessageType`
+
+Перечисление `MessageType` предназначено для классификации сообщений в программном коде. Оно помогает упорядочить типы
+уведомлений и отчетов, выводимых системой. Каждый элемент перечисления обозначает определенный статус или категорию
+сообщения, облегчая чтение и интерпретацию результатов выполнения программ.
+
+## Основная информация
+
+- **Имя файла**: anb_python_components\enums\message_type.py
+- **Автор**: Александр Бабаев
+- **Версия**: 1.0.0
+- **Дата начала поддержки**: с версии 1.0
+
+## Элементы перечисления
+
+- **SUCCESS**: Означает успешное завершение какого-то процесса или операции.
+- **INFO**: Информационные уведомления общего характера.
+- **WARNING**: Предупредительные сообщения, предупреждающие о потенциальных проблемах.
+- **ERROR**: Сигнал о произошедшей ошибке или сбое в выполнении программы.
+
+## Заключение
+
+Перечисление `MessageType` способствует улучшению организации логики сообщений в приложениях, обеспечивая понятное
+разделение на категории, что снижает риск путаницы и улучшает диагностику и тестирование.
+
+[На главную](../../index.md)
\ No newline at end of file
diff --git a/help/class_desc/extensions/guid_extension.md b/help/class_desc/extensions/guid_extension.md
new file mode 100644
index 0000000..0609bec
--- /dev/null
+++ b/help/class_desc/extensions/guid_extension.md
@@ -0,0 +1,92 @@
+# Класс `GUIDExtension`
+
+Класс `GUIDExtension` предназначен для расширения функциональности работы с глобальным уникальным идентификатором
+(GUID). Он предоставляет дополнительные методы для генерации новых GUID, проверки их валидности, парсинга из строк и
+сравнения между собой.
+
+## Основная информация
+
+- **Имя файла**: anb_python_components\extensions\guid_extension.py
+- **Автор**: Александр Бабаев
+- **Версия**: 1.0.0
+- **Дата начала поддержки**: с версии 1.0
+
+## Атрибуты и методы класса
+
+### Метод `generate`
+
+Генерирует новый случайный GUID, следуя стандартам RFC 4122. Новый GUID создается с использованием криптографически
+стойких алгоритмов генерации чисел.
+
+**Пример использования**:
+
+```python
+from anb_python_components.extensions.guid_extension import GUIDExtension
+
+new_guid = GUIDExtension.generate()
+print(new_guid) # Пример: 123e4567-e89b-12d3-a456-426655440000
+```
+
+### Метод `is_equal`
+
+Проверяет равенство двух GUID, поддерживая передачу обоих как строк, так и объектов типа `GUID`.
+
+**Пример использования**:
+
+```python
+from anb_python_components.extensions.guid_extension import GUIDExtension
+from anb_python_components.types.guid import GUID
+
+guid1 = "123e4567-e89b-12d3-a456-426655440000"
+guid2 = GUID("123e4567-e89b-12d3-a456-426655440000")
+
+equal = GUIDExtension.is_equal(guid1, guid2)
+print(equal) # True
+```
+
+### Метод `validate`
+
+Проверяет, соответствует ли строка или объект правильному формату GUID.
+
+**Пример использования**:
+
+```python
+from anb_python_components.extensions.guid_extension import GUIDExtension
+
+valid = GUIDExtension.validate("123e4567-e89b-12d3-a456-426655440000")
+print(valid) # True
+```
+
+### Метод `is_invalid_or_empty`
+
+Проверяет, является ли GUID недействительным или пустым.
+
+**Пример использования**:
+
+```python
+from anb_python_components.extensions.guid_extension import GUIDExtension
+
+invalid = GUIDExtension.is_invalid_or_empty("invalid-guid")
+print(invalid) # True
+```
+
+### Метод `parse`
+
+Парсит строку в GUID, возвращая объект типа `GUID`. Может вернуть пустой GUID, если передано недопустимое значение.
+
+**Пример использования**:
+
+```python
+from anb_python_components.extensions.guid_extension import GUIDExtension
+
+parsed_guid = GUIDExtension.parse("123e4567-e89b-12d3-a456-426655440000")
+print(parsed_guid) # 123e4567-e89b-12d3-a456-426655440000
+```
+
+## Заключение
+
+Класс `GUIDExtension` позволяет создать надежную систему работы с GUID в Python-проектах, облегчая генерацию, проверку и
+сравнение идентификаторов. Благодаря этому обеспечивается дополнительная защита от возможных проблем с передачей
+некорректных данных.
+
+[На главную](../../index.md)
\ No newline at end of file
diff --git a/help/class_desc/models/action_state_message.md b/help/class_desc/models/action_state_message.md
new file mode 100644
index 0000000..a7be049
--- /dev/null
+++ b/help/class_desc/models/action_state_message.md
@@ -0,0 +1,55 @@
+# Класс модели `ActionStateMessage`
+
+Модель `ActionStateMessage` предназначена для формирования сообщений о состоянии выполненных действий в рамках проекта.
+Она содержит основную информацию о результате выполненного действия, включая тип сообщения, текстовое описание и флаги (
+мета-данные).
+
+## Основная информация
+
+- **Имя файла**: anb_python_components\models\action_state_message.py
+- **Автор**: Александр Бабаев
+- **Версия**: 1.0.0
+- **Дата начала поддержки**: с версии 1.0
+
+## Атрибуты и конструктор
+
+### Конструктор (`__init__`)
+
+Принимает три необязательных параметра:
+
+- **`message_type`**: Тип сообщения, использующий перечисление `MessageType` (по умолчанию `INFO`).
+- **`message`**: Сам текст сообщения (по умолчанию пустая строка).
+- **`flags`**: Дополнительные метаданные или признаки сообщения (по умолчанию пустой словарь).
+
+**Пример использования конструктора**:
+
+```python
+from anb_python_components.models.action_state_message import ActionStateMessage, MessageType
+
+state_message = ActionStateMessage(MessageType.SUCCESS, "Действие выполнено успешно.", {"has_errors": False})
+```
+
+## Пример полного использования
+
+Вот пример использования класса `ActionStateMessage`:
+
+```python
+from anb_python_components.models.action_state_message import ActionStateMessage, MessageType
+
+# Создание сообщения успешного действия
+success_message = ActionStateMessage(MessageType.SUCCESS, "Операция завершилась успешно.")
+
+# Создание сообщения с дополнительной информацией
+info_message = ActionStateMessage(MessageType.INFO, "Начало обработки данных.", {"in_progress": False})
+
+# Печать сообщений
+print(success_message.message) # Операция завершилась успешно.
+print(info_message.message) # Начало обработки данных.
+```
+
+## Заключение
+
+Модель `ActionStateMessage` полезна для унификации способов оповещения пользователей или администратора о ходе
+выполнения тех или иных процессов. Четкая структура сообщений облегчает чтение и дальнейшее развитие приложения.
+
+[На главную](../../index.md)
\ No newline at end of file
diff --git a/help/class_desc/types/guid.md b/help/class_desc/types/guid.md
new file mode 100644
index 0000000..c6301b1
--- /dev/null
+++ b/help/class_desc/types/guid.md
@@ -0,0 +1,67 @@
+# Класс `GUID`
+
+Класс `GUID` представляет реализацию уникального глобального идентификатора (Global Unique Identifier), широко
+применяемого в разработке программного обеспечения для уникальной идентификации сущностей. Данный класс добавляет
+дополнительную безопасность и контроль правильности передачи и обработки уникальных идентификаторов.
+
+## Основная информация
+
+- **Имя файла**: anb_python_components\types\guid.py
+- **Автор**: Александр Бабаев
+- **Версия**: 1.0.0
+- **Дата начала поддержки**: с версии 1.0
+
+## Атрибуты и методы класса
+
+### Свойства и конструкторы
+
+- **Атрибут `EMPTY`**: Представляет пустое значение GUID (стандартное значение для незаданных GUID).
+- **Конструктор (`__init__`)**: Осуществляет создание нового экземпляра GUID, принимает либо строку, либо другой
+ экземпляр GUID и проверяет правильность передаваемых данных.
+
+### Специальные методы
+
+- **Метод `__str__`**: Предоставляет текстовое представление текущего GUID.
+- **Метод `__eq__`**: Определяет правило сравнения двух GUID друг с другом.
+
+### Вспомогательные методы
+
+- **Метод `validate_str`**: Проверяет строку на соответствие стандартному формату GUID (регулярные выражения
+ используются для проверки синтаксиса).
+
+## Исключения
+
+- **Ошибка `WrongTypeException`**: Генератор исключений срабатывает при передаче неверного типа данных или нарушении
+ формата GUID.
+
+## Пример использования
+
+Создание экземпляра GUID:
+
+```python
+from anb_python_components.types.guid import GUID
+
+my_guid = GUID("123e4567-e89b-12d3-a456-426655440000")
+print(my_guid) # 123e4567-e89b-12d3-a456-426655440000
+```
+
+Сравнение двух GUID:
+
+```python
+from anb_python_components.types.guid import GUID
+
+first_guid = GUID("123e4567-e89b-12d3-a456-426655440000")
+second_guid = GUID("00000000-0000-0000-0000-000000000000")
+
+if first_guid == second_guid:
+ print("GUID совпадают")
+else:
+ print("GUID различаются")
+```
+
+## Заключение
+
+Класс `GUID` позволяет грамотно управлять уникальным идентификатором, обеспечивая автоматическое преобразование и
+проверку корректности данных. Такой подход минимизирует вероятность ошибок и улучшает надежность приложения.
+
+[На главную](../../index.md)
\ No newline at end of file
diff --git a/help/class_desc/types/two_dim_size.md b/help/class_desc/types/two_dim_size.md
new file mode 100644
index 0000000..fe09b7a
--- /dev/null
+++ b/help/class_desc/types/two_dim_size.md
@@ -0,0 +1,165 @@
+# Класс `TwoDimSize`
+
+Класс `TwoDimSize` предназначен для работы с двумерными размерами, такими как ширина и высота. Он предоставляет ряд
+инструментов для эффективного управления и обработки размеров, включая ограничение минимальных/максимальных границ,
+арифметические операции и удобное представление в строковом виде.
+
+## Основная информация
+
+- **Имя файла**: anb_python_components\types\two_dim_size.py
+- **Автор**: Александр Бабаев
+- **Версия**: 1.0.0
+- **Дата начала поддержки**: с версии 1.0
+
+## Атрибуты и методы класса
+
+### Конструктор (`__init__`)
+
+Принимает начальные значения ширины и высоты, а также минимальные и максимальные границы, если требуется ограничить
+диапазон значений.
+
+**Параметры**:
+
+- `width: int`: Начальная ширина (по умолчанию 0).
+- `height: int`: Начальная высота (по умолчанию 0).
+- `min_width: int | None = 0`, `min_height: int | None = 0`: Минимальное значение ширины и высоты (по умолчанию, `0`).
+ При значении `None` считаются неограниченными.
+- `max_width: int | None = None`, `max_height: int | None = None`: Максимальное значение ширины и высоты (по умолчанию,
+ `None`). При значении `None` считаются неограниченными.
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize(800, 600, min_width = 400, max_width = 1200)
+```
+
+### Свойства класса
+
+Свойства позволяют гибко устанавливать и считывать значения ширины и высоты, автоматически соблюдая введённые
+ограничения. Рассмотрим подробнее каждый из них.
+
+#### Свойство `width`
+
+Управляет значением ширины, выполняя необходимую проверку ограничений при установке.
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize(min_width = 400)
+size.width = 500
+print(size.width) # Вывод: 500
+size.width = 300
+print(size.width) # Вывод: 400
+```
+
+#### Свойство `height`
+
+Аналогично свойствам ширины, управляет значением высоты.
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize(max_height = 1000)
+size.height = 200
+print(size.height) # Вывод: 200
+size.height = 1200
+print(size.height) # Вывод: 1000
+```
+
+#### Свойства границ (`min_width`, `max_width`, `min_height`, `max_height`)
+
+Эти свойства отвечают за изменение граничных значений ширины и высоты, контролируя их согласованность изменений
+(например, максимальная граница не должна быть меньше минимальной).
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize()
+
+# И тут мы вспомнили (а как обычно и бывает!), что забыли дать ограничения! =)
+# Установим минимальную ширину
+size.min_width = 500
+# и максимальную высоту
+size.max_height = 1000
+
+# Включаем внутреннего Пушного и запускаем рубрику "Эксперименты"
+size.height = 600
+size.width = 600
+print(str(size)) # Вывод: 600:600
+size.height = 900
+size.width = 100
+print(str(size)) # Вывод: 500:900
+# Мы в этом примере задали число, меньшее минимальной ширины, вот она и вернула минимальную.
+# Так же будет, если мы превысим максимальную высоту
+size.height = 1200
+size.width = 600
+print(str(size)) # Вывод: 600:1000
+# И даже если не соблюдём оба
+size.height = 100
+size.width = 1200
+print(str(size)) # Вывод: 500:1000
+```
+
+### Ключевые методы
+
+#### Метод `as_str`
+
+Форматирует объект в строку вида `"ширина:высота"`, используя разделитель (по умолчанию двоеточие).
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize(800, 600)
+print(size.as_str()) # Вывод: 800:600
+```
+
+#### Метод `as_tuple`
+
+Возвращает размеры в виде кортежа `(ширина, высота)`.
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize(800, 600)
+print(size.as_tuple()) # Вывод: (800, 600)
+```
+
+#### Метод `parse`
+
+Анализирует строку вида `"ширина:высота"` и создает объект `TwoDimSize` из нее.
+
+**Пример использования**:
+
+```python
+from anb_python_components.types.two_dim_size import TwoDimSize
+
+size = TwoDimSize.parse("1920:1080")
+print(size.width) # 1920
+print(size.height) # 1080
+```
+
+### Магические методы
+
+- `__str__`, `__repr__`: Отвечают за отображение объекта в строковом виде.
+- `__eq__`: Определяет равенство двух объектов по ширине и высоте.
+- `__hash__`: Позволяет использовать объект в структурах вроде множества или словаря.
+- `__add__`, `__mul__`: Поддерживают сложение и умножение размеров соответственно.
+
+## Заключение
+
+Класс `TwoDimSize` идеально подходит для работы с размерами графических объектов, элементами UI и подобными ситуациями,
+требующими четкого контроля за пределами размеров и легкости манипуляций.
+
+[На главную](../../index.md)
\ No newline at end of file
diff --git a/help/index.md b/help/index.md
index 464d54c..8121d30 100644
--- a/help/index.md
+++ b/help/index.md
@@ -8,11 +8,18 @@
## Описание интерфейсов, классов и перечислений:
+### Классы
+
+**Расположение модулей**: anb_python_components\classes\*
+
+- [класс `ActionState`](class_desc/classes/action_state.md)
+
### Перечисления
**Расположение модулей**: anb_python_components\enums\*
- [перечисление `NotBoolAction`](class_desc/enums/not_bool_action.md)
+- [перечисление `MessageType`](class_desc/enums/message_type.md)
### Исключения
@@ -28,4 +35,18 @@
- [класс `StringExtension`](class_desc/extensions/string_extension.md)
- [класс `BoolExtension`](class_desc/extensions/bool_extension.md)
- [класс `TypeExtension`](class_desc/extensions/type_extension.md)
-- [класс `ArrayExtension`](class_desc/extensions/array_extension.md)
\ No newline at end of file
+- [класс `ArrayExtension`](class_desc/extensions/array_extension.md)
+- [класс `GUIDExtension`](class_desc/extensions/guid_extension.md)
+
+### Типы
+
+**Расположение модулей**: anb_python_components\types\*
+
+- [тип `GUID`](class_desc/types/guid.md)
+- [тип `TwoDimSize`](class_desc/types/two_dim_size.md)
+
+### Модели
+
+**Расположение модулей**: anb_python_components\models\*
+
+- [модель `ActionStateMessage`](class_desc/models/action_state_message.md)
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
index 4a50878..4f54c83 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1 +1 @@
-# anb_python_components/__init__.py
\ No newline at end of file
+# tests/__init__.py
\ No newline at end of file
diff --git a/tests/classes/__init__.py b/tests/classes/__init__.py
new file mode 100644
index 0000000..1fb4c98
--- /dev/null
+++ b/tests/classes/__init__.py
@@ -0,0 +1 @@
+# tests/classes/__init__.py
\ No newline at end of file
diff --git a/tests/classes/action_state_test.py b/tests/classes/action_state_test.py
new file mode 100644
index 0000000..46283e7
--- /dev/null
+++ b/tests/classes/action_state_test.py
@@ -0,0 +1,138 @@
+# action_state_test.py
+
+import unittest
+
+from anb_python_components.classes.action_state import ActionState, ActionStateMessage, MessageType
+
+class ActionStateTest(unittest.TestCase):
+ def test_init (self):
+ state = ActionState[bool](False)
+ self.assertIsInstance(state, ActionState)
+ self.assertFalse(state.value)
+ state.value = True
+ self.assertTrue(state.value)
+
+ def test_add_message (self):
+ message = ActionStateMessage(MessageType.INFO, "Test message")
+
+ state = ActionState[bool](False)
+ state.add_message(message)
+
+ self.assertEqual(1, state.count())
+
+ @staticmethod
+ def get_test_state (no_warning: bool = False, no_error: bool = False, state_value: bool = False) -> ActionState[
+ bool]:
+ """
+ Генерирует тестовое состояние.
+ :param no_warning: Без предупреждений.
+ :param no_error: Без ошибок.
+ :param state_value: Значение состояния.
+ :return: Тестовое состояние.
+ """
+ state = ActionState[bool](False)
+
+ state.add_info("Тестовое сообщение1")
+ if not no_error:
+ state.add_error("Тестовое сообщение2")
+ state.add_info("Тестовое сообщение3")
+ state.add_info("Тестовое сообщение4")
+ if not no_warning:
+ state.add_warning("Тестовое сообщение5")
+ state.add_info("Тестовое сообщение6")
+ state.add_info("Тестовое сообщение7")
+ state.add_info("Тестовое сообщение8")
+ if not no_warning:
+ state.add_warning("Тестовое сообщение9")
+ if not no_error:
+ state.add_error("Тестовое сообщение10")
+
+ state.value = state_value
+
+ return state
+
+ def test_add_state (self):
+ state1 = ActionStateTest.get_test_state(True, True, True)
+
+ state2 = ActionStateTest.get_test_state(state_value = False)
+
+ state1.add_state(state2)
+
+ self.assertEqual(16, state1.count())
+
+ def test_get_messages (self):
+ state = ActionStateTest.get_test_state()
+
+ state_messages = state.get_messages()
+
+ self.assertEqual(10, len(state_messages))
+
+ count_errors = 0
+
+ for message in state_messages:
+ if message.message_type == MessageType.ERROR:
+ count_errors += 1
+
+ self.assertEqual(2, count_errors)
+
+ def test_get_string_messages (self):
+ state = ActionStateTest.get_test_state()
+
+ state_message_string = state.get_string_messages(ActionState.get_string_error_only())
+
+ need_string = "Тестовое сообщение2\nТестовое сообщение10"
+
+ self.assertEqual(state_message_string, need_string)
+
+ def test_has_infos (self):
+ state = ActionStateTest.get_test_state()
+
+ self.assertTrue(state.has_infos())
+
+ def test_has_warnings (self):
+ state = ActionStateTest.get_test_state()
+
+ self.assertTrue(state.has_warnings())
+
+ def test_has_errors (self):
+ state = ActionStateTest.get_test_state()
+
+ self.assertTrue(state.has_errors())
+
+ def test_is_success (self):
+ state_fail = ActionStateTest.get_test_state()
+ state_success = ActionStateTest.get_test_state(no_warning = True, no_error = True)
+ state_success_no_warning = ActionStateTest.get_test_state(no_error = True)
+
+ self.assertTrue(state_success.is_success())
+ self.assertTrue(state_success_no_warning.is_success(True))
+ self.assertFalse(state_fail.is_success())
+
+ def test_clear (self):
+ state = ActionStateTest.get_test_state()
+
+ state.clear(lambda message: message.message_type == MessageType.WARNING)
+
+ self.assertEqual(8, len(state.get_messages()))
+
+ state.clear()
+
+ self.assertEqual(0, len(state.get_messages()))
+
+ def test_count (self):
+ state = ActionStateTest.get_test_state()
+
+ count_all = state.count()
+ count_warnings = state.count(lambda message: message.message_type == MessageType.WARNING)
+ count_errors = state.count(lambda message: message.message_type == MessageType.ERROR)
+ count_errors_and_warnings = state.count(
+ lambda message: message.message_type == MessageType.WARNING or message.message_type == MessageType.ERROR
+ )
+
+ self.assertEqual(10, count_all)
+ self.assertEqual(2, count_errors)
+ self.assertEqual(2, count_warnings)
+ self.assertEqual(4, count_errors_and_warnings)
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
diff --git a/tests/extensions/__init__.py b/tests/extensions/__init__.py
index 7ba3d65..0ed9bd6 100644
--- a/tests/extensions/__init__.py
+++ b/tests/extensions/__init__.py
@@ -1 +1 @@
-# anb_python_components/extensions/__init__.py
\ No newline at end of file
+# tests/extensions/__init__.py
\ No newline at end of file
diff --git a/tests/extensions/bool_extension_test.py b/tests/extensions/bool_extension_test.py
index 301627a..bbf1f05 100644
--- a/tests/extensions/bool_extension_test.py
+++ b/tests/extensions/bool_extension_test.py
@@ -1,21 +1,19 @@
-# string_extension_test.py
+# bool_extension_test.py
import unittest
from anb_python_components.extensions.bool_extension import *
-
class BoolExtensionTest(unittest.TestCase):
- def test_to_str(self):
+ def test_to_str (self):
self.assertEqual(BoolExtension.to_str(True, "да", "нет"), "да")
self.assertEqual(BoolExtension.to_str(False, "да", "нет"), "нет")
-
- def test_true_count(self):
+
+ def test_true_count (self):
self.assertEqual(BoolExtension.true_count([False, True, False, True, True, False, False]), 3)
-
- def test_any_true(self):
+
+ def test_any_true (self):
self.assertTrue(BoolExtension.any_true([False, True, False, True, True, False, False]))
-
if __name__ == '__main__':
- unittest.main()
+ unittest.main()
\ No newline at end of file
diff --git a/tests/types/__init__.py b/tests/types/__init__.py
new file mode 100644
index 0000000..c77facc
--- /dev/null
+++ b/tests/types/__init__.py
@@ -0,0 +1 @@
+# tests/types/__init__.py
\ No newline at end of file
diff --git a/tests/types/guid_test.py b/tests/types/guid_test.py
new file mode 100644
index 0000000..bf63a82
--- /dev/null
+++ b/tests/types/guid_test.py
@@ -0,0 +1,78 @@
+# anb_python_components/types/guid.py
+import re
+
+from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
+
+class GUID:
+ """
+ Тип GUID.
+ """
+
+ # Константа пустого GUID
+ EMPTY: str = "00000000-0000-0000-0000-000000000000"
+
+ def __init__ (self, guid = EMPTY):
+ """
+ Инициализация расширения.
+ :param guid: str | GUID - Передаваемый GUID
+ """
+ # Проверка типа аргумента guid
+ # - если аргумент не является строкой
+ if not isinstance(guid, str):
+ # -- если аргумент является экземпляром GUID
+ if isinstance(guid, GUID):
+ # - преобразуем его в строку
+ guid = str(guid)
+ else:
+ # -- иначе генерируем исключение
+ raise WrongTypeException("Неверный тип аргумента!", "GUID", str(type(guid)), "guid")
+
+ # Проверка GUID на валидность
+ if not self.validate_str(guid):
+ # и если GUID невалидный, то генерируем исключение
+ raise WrongTypeException("Неверный формат GUID!", "GUID", guid, "guid")
+
+ # Инициализируем приватный атрибут __value (текстовое представление хранящегося GUID)
+ self.__value = guid
+
+ def __str__ (self):
+ """
+ Переопределение метода __str__.
+ :return: Текстовое представление GUID.
+ """
+ return self.__value if self.__value else self.EMPTY
+
+ def __eq__ (self, other):
+ """
+ Переопределение метода __eq__.
+ :param other: Объект для сравнения.
+ :return: True, если GUID равны, иначе False.
+ """
+
+ # Если аргумент не является экземпляром GUID
+ if not isinstance(other, GUID):
+ # - генерируем исключение
+ raise WrongTypeException("Неверный тип аргумента!", "GUID", type(other), "other")
+
+ # Преобразование второго аргумента в строку
+ other_str = str(other)
+
+ # Сравниваем строки
+ return self.__value == other_str
+
+ @staticmethod
+ def validate_str (guid: str | None) -> bool:
+ """
+ Проверка строки на валидность GUID.
+ :return: True, если GUID валидный, иначе False.
+ """
+
+ # Проверка на пустоту
+ if not guid:
+ return False
+
+ # Регулярное выражение для проверки формата GUID
+ pattern = r'^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$'
+
+ # Проверка на соответствие формату
+ return bool(re.fullmatch(pattern, guid))
\ No newline at end of file
diff --git a/tests/types/two_dim_size_test.py b/tests/types/two_dim_size_test.py
new file mode 100644
index 0000000..f7909f9
--- /dev/null
+++ b/tests/types/two_dim_size_test.py
@@ -0,0 +1 @@
+# two_dim_size_test.py
\ No newline at end of file
diff --git a/tests/types/version_info_test.py b/tests/types/version_info_test.py
new file mode 100644
index 0000000..ee0af7e
--- /dev/null
+++ b/tests/types/version_info_test.py
@@ -0,0 +1,51 @@
+# version_info_test.py
+
+import unittest
+
+from anb_python_components.types.version_info import VersionInfo
+
+class VersionInfoTest(unittest.TestCase):
+ def test_init (self):
+ version_info = VersionInfo(1, 2, 3, 4, "Тестовая версия", 1)
+ version_info_str = '1.2.3.4 Тестовая версия 1'
+
+ self.assertEqual(version_info_str, str(version_info))
+
+ def test_math (self):
+ version_info_1 = VersionInfo(1, 2, 3, 4, "Тестовая версия", 1)
+ version_info_2 = VersionInfo(1, 2, 3, 4, "Тестовая версия", 1)
+ version_info_3 = VersionInfo(2, 1, 3, 10, "Тестовая версия", 2)
+ version_info_4 = VersionInfo(3, 5, 3, 12, "Тестовая версия", 3)
+
+ self.assertTrue(version_info_1 == version_info_2)
+ self.assertTrue(version_info_3 > version_info_2)
+ self.assertTrue(version_info_3 >= version_info_1)
+ self.assertTrue(version_info_1 < version_info_4)
+ self.assertTrue(version_info_3.in_range(version_info_1, version_info_4))
+ self.assertFalse(version_info_3.in_range(version_info_1, version_info_3, end_inclusive = False))
+ self.assertTrue(version_info_3.in_range(version_info_1))
+ self.assertTrue(version_info_3.in_range())
+
+ def test_parse (self):
+ str_ver_1 = '1.2.3.4 Тестовая 1'
+ version_info_1 = VersionInfo(1, 2, 3, 4, "Тестовая", 1)
+ str_ver_2 = "1.2.3.4 Тестовая"
+ version_info_2 = VersionInfo(1, 2, 3, 4, "Тестовая", 0)
+ str_ver_3 = "1.2.3.4"
+ version_info_3 = VersionInfo(1, 2, 3, 4, "", 0)
+ str_ver_4 = "1.2.3 Тестовая 1"
+ version_info_4 = VersionInfo(1, 2, 3, 0, "Тестовая", 1)
+ str_ver_5 = "1.2 Тестовая 1"
+ version_info_5 = VersionInfo(1, 2, 0, 0, "Тестовая", 1)
+ str_ver_6 = "1 Тестовая 1"
+ version_info_6 = VersionInfo(1, 0, 0, 0, "Тестовая", 1)
+
+ self.assertEqual(version_info_1, VersionInfo.parse(str_ver_1))
+ self.assertEqual(version_info_2, VersionInfo.parse(str_ver_2))
+ self.assertEqual(version_info_3, VersionInfo.parse(str_ver_3))
+ self.assertEqual(version_info_4, VersionInfo.parse(str_ver_4))
+ self.assertEqual(version_info_5, VersionInfo.parse(str_ver_5))
+ self.assertEqual(version_info_6, VersionInfo.parse(str_ver_6))
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file