package service import ( "fmt" "hy2xs-admin/model/constant" ) // Доменные отказы пиров несут КОД, а не только текст. // // Раньше причина отказа существовала исключительно в виде человеческой фразы: // `fmt.Errorf("name %s already exists", …)`. Слой контроллеров отдавал её // панели как есть, панель показывала её тостом, и всё, что могло бы захотеть // отреагировать на причину — подсветить нужное поле формы, перевести фразу на // язык оператора, — было вынуждено сравнивать текст. Такое сравнение молча // ломается при первой же правке формулировки. // // Код и имя поля объявляются здесь, рядом с местом, которое отказ порождает. // PeerError — отказ операции над пиром с машиночитаемой причиной. type PeerError struct { // Code — причина из constant.ErrCode*. Code string // Field — поле формы, к которому относится отказ; пусто, если отказ // относится к операции целиком. Field string // Message — человекочитаемое объяснение для клиента без UI. Message string } func (e *PeerError) Error() string { return e.Message } // ErrPeerNameRequired — имя пира не передано. // // Правило `required` в DTO ловит это раньше, но CreatePeer вызывается и из // тестов, и потенциально из будущих внутренних путей, поэтому проверка // остаётся, а не превращается в предположение. var ErrPeerNameRequired = &PeerError{ Code: constant.ErrCodeRequired, Field: "name", Message: "имя пира обязательно", } // ErrPeerNameReserved — имя принадлежит пиру установщика. // // Имя зарезервировано и когда сам пир уже удалён: иначе обычный пир занял бы // его и оказался под защитой, предназначенной не ему. var ErrPeerNameReserved = &PeerError{ Code: constant.ErrCodePeerNameReserved, Field: "name", Message: fmt.Sprintf( "имя %q зарезервировано за пиром установщика", ReservedBootstrapPeerName, ), } // PeerNameTakenError — имя уже занято другим пиром. func PeerNameTakenError(name string) *PeerError { return &PeerError{ Code: constant.ErrCodePeerNameTaken, Field: "name", Message: fmt.Sprintf("пир с именем %q уже существует", name), } }