Initial commit
This commit is contained in:
0
hw09_struct_validator/.sync
Normal file
0
hw09_struct_validator/.sync
Normal file
65
hw09_struct_validator/README.md
Normal file
65
hw09_struct_validator/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
## Домашнее задание №9 «Валидатор структур»
|
||||
|
||||
Необходимо реализовать функцию:
|
||||
```golang
|
||||
func Validate(v interface{}) error
|
||||
```
|
||||
Она должна валидировать публичные поля входной структуры на основе структурного тэга `validate`.
|
||||
|
||||
Функция может возвращать
|
||||
- или программную ошибку, произошедшую во время валидации;
|
||||
- или `ValidationErrors` - ошибку, являющуюся слайсом структур, содержащих имя поля и ошибку его валидации.
|
||||
|
||||
Таким образом, нужно накопить все ошибки валидации, а не прерывать валидацию на первой ошибке.
|
||||
|
||||
Если у поля нет структурных тэгов или нет тэга `validate`, то функция игнорирует его.
|
||||
|
||||
Типы полей, которые обязательно должны поддерживаться:
|
||||
- `int`, `[]int`;
|
||||
- `string`, `[]string`.
|
||||
|
||||
_При желании можно дополнительно поддержать любые другие типы (на ваше усмотрение)._
|
||||
|
||||
Необходимо реализовать следующие валидаторы:
|
||||
- Для строк:
|
||||
* `len:32` - длина строки должна быть ровно 32 символа;
|
||||
* `regexp:\\d+` - согласно регулярному выражению строка должна состоять из цифр
|
||||
(`\\` - экранирование слэша);
|
||||
* `in:foo,bar` - строка должна входить в множество строк {"foo", "bar"}.
|
||||
- Для чисел:
|
||||
* `min:10` - число не может быть меньше 10;
|
||||
* `max:20` - число не может быть больше 20;
|
||||
* `in:256,1024` - число должно входить в множество чисел {256, 1024};
|
||||
- Для слайсов валидируется каждый элемент слайса.
|
||||
|
||||
_При желании можно дополнительно добавить парочку новых правил (на ваше усмотрение)._
|
||||
|
||||
Допускается комбинация валидаторов по логическому "И" с помощью `|`, например:
|
||||
* `min:0|max:10` - число должно находится в пределах [0, 10];
|
||||
* `regexp:\\d+|len:20` - строка должна состоять из цифр и иметь длину 20.
|
||||
|
||||
**(\*) Дополнительное задание: поддержка валидации вложенных по композиции структур.**
|
||||
```golang
|
||||
type User struct {
|
||||
m Meta `validate:"nested"`
|
||||
}
|
||||
```
|
||||
|
||||
### Критерии оценки
|
||||
- Пайплайн зелёный - 3 балла
|
||||
- Добавлены юнит-тесты - до 4 баллов
|
||||
- Понятность и чистота кода - до 3 баллов
|
||||
- Дополнительное задание на баллы не влияет
|
||||
|
||||
#### Зачёт от 7 баллов
|
||||
|
||||
### Подсказки
|
||||
- `reflect.StructTag`
|
||||
- `regexp.Compile`
|
||||
- `errors.Is`
|
||||
|
||||
### Частые ошибки
|
||||
- Отсутствует проверка на то, что входной `interface{}` - структура.
|
||||
- Нет разделения на программные ошибки (неверный тэг, регулярка и пр.) и ошибки валидации.
|
||||
- Ошибки валидации не вынесены в отдельные переменные и не завраплены.
|
||||
- Соответственно в тестах не используется `errors.Is` / `errors.As`.
|
||||
3
hw09_struct_validator/go.mod
Normal file
3
hw09_struct_validator/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/fixme_my_friend/hw09_struct_validator
|
||||
|
||||
go 1.19
|
||||
17
hw09_struct_validator/validator.go
Normal file
17
hw09_struct_validator/validator.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package hw09structvalidator
|
||||
|
||||
type ValidationError struct {
|
||||
Field string
|
||||
Err error
|
||||
}
|
||||
|
||||
type ValidationErrors []ValidationError
|
||||
|
||||
func (v ValidationErrors) Error() string {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func Validate(v interface{}) error {
|
||||
// Place your code here.
|
||||
return nil
|
||||
}
|
||||
60
hw09_struct_validator/validator_test.go
Normal file
60
hw09_struct_validator/validator_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package hw09structvalidator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type UserRole string
|
||||
|
||||
// Test the function on different structures and other types.
|
||||
type (
|
||||
User struct {
|
||||
ID string `json:"id" validate:"len:36"`
|
||||
Name string
|
||||
Age int `validate:"min:18|max:50"`
|
||||
Email string `validate:"regexp:^\\w+@\\w+\\.\\w+$"`
|
||||
Role UserRole `validate:"in:admin,stuff"`
|
||||
Phones []string `validate:"len:11"`
|
||||
meta json.RawMessage //nolint:unused
|
||||
}
|
||||
|
||||
App struct {
|
||||
Version string `validate:"len:5"`
|
||||
}
|
||||
|
||||
Token struct {
|
||||
Header []byte
|
||||
Payload []byte
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
Response struct {
|
||||
Code int `validate:"in:200,404,500"`
|
||||
Body string `json:"omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
in interface{}
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
// Place your code here.
|
||||
},
|
||||
// ...
|
||||
// Place your code here.
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
|
||||
tt := tt
|
||||
t.Parallel()
|
||||
|
||||
// Place your code here.
|
||||
_ = tt
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user