pagetoken

package
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package pagetoken — ЕДИНСТВЕННОЕ объявление формы курсора страницы.

Зачем пакет существует

Форма токена была записана в дереве трижды: авторитетный кодек в слое репозитория, рукописное зеркало в проверке формата на границе приложения — и оно бежит ПЕРВЫМ — и свой декодер в общем пакете операций. Четвёртая копия жила в другом сервисе.

Пока значения совпадают, это не расхождение, а заряженная ловушка: смена формы вносится в три места, и первое же пропущенное даст ответ, зависящий от того, какой из декодеров бежал раньше. Оба при этом отвечают «валидно» на валидном входе — то есть расходятся ровно там, где расхождение не видно.

Конвенция требует этого прямо: «кодек курсора не переписывать — guard обязан звать тот же разбор, что исполняется на пути чтения».

Почему в фундаменте, а не в слое приложения

Кодек зовут ОБА слоя: проверка формата на границе приложения и репозиторий. Репозиторий — адаптер, и импортировать слой use-case ему нельзя (правило направления зависимостей). Единственное место, видимое обоим и ни одному не принадлежащее, — фундамент.

Index

Constants

This section is empty.

Variables

View Source
var Canonical = Codec{/* contains filtered or unexported fields */}

Canonical — форма iam: стандартный алфавит с дополнением, разделитель `|`.

View Source
var NULSeparatedRawURL = Codec{/* contains filtered or unexported fields */}

NULSeparatedRawURL — форма nlb: алфавит URL без дополнения, разделитель — нулевой байт. Объявлена здесь, а не у nlb, ровно затем, чтобы РАЗЛИЧИЕ ДВУХ ФОРМ было видно в одном месте: пока каждая жила у себя, несовместимость обнаруживалась только на чужом токене.

Functions

func Encode

func Encode(c Cursor) string

Encode собирает токен канонической формы.

func EncodeSubscriptionPosition

func EncodeSubscriptionPosition(p SubscriptionPosition) string

EncodeSubscriptionPosition собирает непрозрачный токен позиции.

Пустой строки конструктор НЕ выпускает НИКОГДА: пустое значение поля позиции означает «позиция не задана», и производитель, способный выпустить пустое, сделал бы эти два состояния неразличимыми у вызывающего.

func SubscriptionPositionWellFormed

func SubscriptionPositionWellFormed(token string) bool

SubscriptionPositionWellFormed отвечает, разбирается ли токен. Пустой — да («позиция не задана»).

Отдельная функция нужна проверке формата на границе приложения: ей позиция не нужна, нужен вердикт, — а повторять разбор своим кодом есть ровно то, чего пакет и избегает.

func WellFormed

func WellFormed(token string) bool

WellFormed отвечает, разбирается ли токен. Пустой — да (первая страница).

Отдельная функция нужна проверке формата: ей курсор не нужен, нужен вердикт, а повторять разбор своим кодом — ровно то, чего пакет и избегает.

Types

type Codec

type Codec struct {
	// contains filtered or unexported fields
}

Codec — объявленная форма курсора: чем кодируется и чем разделяется тело.

func (Codec) Decode

func (k Codec) Decode(token string) (*Cursor, bool)

Decode разбирает токен этой формы.

func (Codec) Encode

func (k Codec) Encode(c Cursor) string

Encode собирает токен этой формы.

type Cursor

type Cursor struct {
	CreatedAt time.Time
	ID        string
}

Cursor — граница страницы в keyset-порядке: последняя ОТДАННАЯ строка.

func Decode

func Decode(token string) (*Cursor, bool)

Decode разбирает токен.

Пустой токен — ПЕРВАЯ страница, и он возвращает (nil, true): отсутствие представимо отдельно от всякого значения, поэтому вызывающий не обязан различать «токена не было» и «токен разобрался в нулевой курсор».

Второй ответ — годность, а не ошибка: у вызывающих разный контракт отказа (проверка формата отвечает INVALID_ARGUMENT с именем поля, репозиторий — своим сигнальным значением), и навязывать им общий тип ошибки значило бы заставить каждого переводить её обратно.

type SubscriptionPosition

type SubscriptionPosition struct {
	// Settled — граница устоявшегося. Ноль — законная величина: «журнал ещё
	// ничего не устоял».
	Settled int64
}

SubscriptionPosition — позиция подписки: граница УСТОЯВШЕГОСЯ в журнале владельца.

Почему это ОТДЕЛЬНЫЙ тип, а не [Cursor]

`Cursor` объявлен как «граница страницы в keyset-порядке: последняя ОТДАННАЯ строка». Для подписки это дословно запрещённая форма: номер выдаётся на вставке, видимость наступает на фиксации, поэтому писатель, закоммитивший позже с меньшим номером, оказывается ЗА границей по отданному — навсегда, молча и без пропуска в нумерации, видимого клиенту.

Взять `Cursor` было бы унификацией по самой узкой семантике: два предмета совпадают формой («строка-токен, кодирующая место в порядке») и расходятся смыслом границы. Совпадение формы не есть общность предмета.

Что означает Settled

Каждый номер `≤ Settled` в журнале владельца либо УЖЕ ВИДИМ, либо не появится НИКОГДА (писатель откатился). Это и есть свойство, ради которого позиция непрозрачна: возобновление с неё не пропускает ни одной строки, закоммиченной после её выдачи. Производит эту величину сервер подписки (`pkg/subscription`); кодек её только переносит.

Скаляр здесь законен — незаконен его ПРОИЗВОДИТЕЛЬ «максимум видимого». Разрядность величины ни при чём.

func DecodeSubscriptionPosition

func DecodeSubscriptionPosition(token string) (*SubscriptionPosition, bool)

DecodeSubscriptionPosition разбирает токен позиции.

Пустой токен — «позиция не задана», и он возвращает `(nil, true)`: отсутствие представимо ОТДЕЛЬНО от всякого значения, поэтому вызывающий не обязан различать «позиции не было» и «позиция разобралась в ноль». Ноль здесь — законная величина («ничего ещё не устоялось»), и слить его с отсутствием значило бы отдать журнал с начала тому, кто просил хвост.

Второй ответ — годность, а не ошибка: у вызывающих разный контракт отказа, и навязывать им общий тип значило бы заставить каждого переводить его обратно.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL