shared

package
v0.38.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	ErrCodeFlowNotFound                = 40001
	ErrCodeFlowNotActive               = 40002
	ErrCodeNoPublishedVersion          = 40003
	ErrCodeVersionNotDraft             = 40004
	ErrCodeInvalidFlowDesign           = 40005
	ErrCodeFlowCodeExists              = 40006
	ErrCodeVersionNotFound             = 40007
	ErrCodeInvalidBusinessIdentifier   = 40008
	ErrCodeInvalidTitleTemplate        = 40009
	ErrCodeInvalidFormDesign           = 40010
	ErrCodeBindingIncomplete           = 40011
	ErrCodeInvalidBindingMode          = 40012
	ErrCodeInvalidInitiatorKind        = 40013
	ErrCodeInvalidStorageMode          = 40014
	ErrCodeFlowBindingLocked           = 40015
	ErrCodeBindingColumnsConflict      = 40016
	ErrCodeBindingUnexpected           = 40017
	ErrCodeBindingSchemaInvalid        = 40018
	ErrCodeBindingKeyNotUnique         = 40019
	ErrCodeBindingStatusMappingInvalid = 40020

	ErrCodeInstanceNotFound          = 40101
	ErrCodeInstanceCompleted         = 40102
	ErrCodeNotAllowedInitiate        = 40103
	ErrCodeWithdrawNotAllowed        = 40104
	ErrCodeResubmitNotAllowed        = 40105
	ErrCodeInvalidInstanceTransition = 40106
	ErrCodeBusinessRefRequired       = 40107
	ErrCodeBindingTargetBusy         = 40108
	ErrCodeInvalidBusinessRef        = 40109
	ErrCodeBindingProjectionNotFound = 40110

	ErrCodeTaskNotFound             = 40201
	ErrCodeTaskNotPending           = 40202
	ErrCodeNotAssignee              = 40203
	ErrCodeInvalidTaskTransition    = 40204
	ErrCodeRollbackNotAllowed       = 40205
	ErrCodeAddAssigneeNotAllowed    = 40206
	ErrCodeTransferNotAllowed       = 40207
	ErrCodeOpinionRequired          = 40208
	ErrCodeManualCcNotAllowed       = 40209
	ErrCodeRemoveAssigneeNotAllowed = 40210
	ErrCodeInvalidAddAssigneeType   = 40211
	ErrCodeNotApplicant             = 40212
	ErrCodeInvalidRollbackTarget    = 40213
	ErrCodeLastAssigneeRemoval      = 40214
	ErrCodeInvalidTransferTarget    = 40215
	ErrCodeNoUsersSpecified         = 40216

	ErrCodeNoAssignee            = 40301
	ErrCodeAssigneeResolveFailed = 40302

	ErrCodeFormValidationFailed = 40401

	ErrCodeUrgeCooldown = 40601

	ErrCodeAccessDenied        = 40701
	ErrCodeTerminateNotAllowed = 40702
)

Error codes for the approval module (40xxx range).

View Source
const (
	// Urge errors. Template parameters:
	//   {{.minutes}} — cooldown window in minutes
	ErrMessageUrgeTooFrequent = "approval_urge_too_frequent"

	// Form field validation errors. Template parameters:
	//   {{.field}} — field label or key
	//   {{.min}}   — minimum length / value
	//   {{.max}}   — maximum length / value
	ErrMessageFormFieldNotDefined        = "approval_form_field_not_defined"
	ErrMessageFormFieldRequired          = "approval_form_field_required"
	ErrMessageFormFieldMustBeString      = "approval_form_field_must_be_string"
	ErrMessageFormFieldMustBeNumber      = "approval_form_field_must_be_number"
	ErrMessageFormFieldMustBeInteger     = "approval_form_field_must_be_integer"
	ErrMessageFormFieldMinLength         = "approval_form_field_min_length"
	ErrMessageFormFieldMaxLength         = "approval_form_field_max_length"
	ErrMessageFormFieldInvalidValidation = "approval_form_field_invalid_validation"
	ErrMessageFormFieldPatternMismatch   = "approval_form_field_pattern_mismatch"
	ErrMessageFormFieldMinValue          = "approval_form_field_min_value"
	ErrMessageFormFieldMaxValue          = "approval_form_field_max_value"
	ErrMessageFormFieldEmpty             = "approval_form_field_empty"
	ErrMessageFormFieldInvalidFileItem   = "approval_form_field_invalid_file_item"
	ErrMessageFormFieldMustBeFile        = "approval_form_field_must_be_file"
	ErrMessageFormFieldInvalidValue      = "approval_form_field_invalid_value"
	ErrMessageFormFieldMustBeRowList     = "approval_form_field_must_be_row_list"
	ErrMessageFormFieldMustBeRowObject   = "approval_form_field_must_be_row_object"
	ErrMessageFormFieldMinRows           = "approval_form_field_min_rows"
	ErrMessageFormFieldMaxRows           = "approval_form_field_max_rows"
	ErrMessageFormFieldTableCell         = "approval_form_field_table_cell"
)

Message IDs for the approval module's i18n keys. Only constants referenced cross-file (template params, factory errors, or mapping tables) are defined here. Single-use sentinel keys are inlined directly at their result.Err definition in api_errors.go.

Variables

View Source
var (
	ErrFlowNotFound       = result.Err(i18n.T("approval_flow_not_found"), result.WithCode(ErrCodeFlowNotFound))
	ErrFlowNotActive      = result.Err(i18n.T("approval_flow_not_active"), result.WithCode(ErrCodeFlowNotActive))
	ErrNoPublishedVersion = result.Err(i18n.T("approval_no_published_version"), result.WithCode(ErrCodeNoPublishedVersion))
	ErrVersionNotDraft    = result.Err(i18n.T("approval_version_not_draft"), result.WithCode(ErrCodeVersionNotDraft))
	ErrInvalidFlowDesign  = result.Err(i18n.T("approval_invalid_flow_design"), result.WithCode(ErrCodeInvalidFlowDesign))
	ErrFlowCodeExists     = result.Err(i18n.T("approval_flow_code_exists"), result.WithCode(ErrCodeFlowCodeExists))
	ErrVersionNotFound    = result.Err(i18n.T("approval_version_not_found"), result.WithCode(ErrCodeVersionNotFound))
	// ErrInvalidBusinessIdentifier rejects dynamic business table / column names
	// that do not match the strict SQL-identifier policy used by ORM queries.
	ErrInvalidBusinessIdentifier = result.Err(
		i18n.T("approval_invalid_business_identifier"),
		result.WithCode(ErrCodeInvalidBusinessIdentifier),
	)
	// ErrInvalidTitleTemplate rejects an instance title template that does
	// not parse as a Go text/template at flow create / update time, so a
	// broken template cannot silently break every subsequent submission.
	ErrInvalidTitleTemplate = result.Err(i18n.T("approval_invalid_title_template"), result.WithCode(ErrCodeInvalidTitleTemplate))
	// ErrInvalidFormDesign rejects a structurally broken form schema at
	// deploy time (duplicate keys, unknown field kind, uncompilable
	// validation pattern) so configuration faults never surface as data
	// errors to the applicant.
	ErrInvalidFormDesign = result.Err(i18n.T("approval_invalid_form_design"), result.WithCode(ErrCodeInvalidFormDesign))
	// ErrBindingIncomplete rejects a BindingMode=business flow missing its table,
	// key columns, status column, or instance-ID fencing column at save time.
	ErrBindingIncomplete = result.Err(i18n.T("approval_binding_incomplete"), result.WithCode(ErrCodeBindingIncomplete))

	// ErrInvalidBindingMode rejects an out-of-enum flow binding mode at save
	// time — an unknown value would silently behave like "standalone" and
	// disable the business write-back.
	ErrInvalidBindingMode = result.Err(i18n.T("approval_invalid_binding_mode"), result.WithCode(ErrCodeInvalidBindingMode))

	// ErrInvalidInitiatorKind rejects an out-of-enum initiator kind at save
	// time — an unknown value would silently never match any user.
	ErrInvalidInitiatorKind = result.Err(i18n.T("approval_invalid_initiator_kind"), result.WithCode(ErrCodeInvalidInitiatorKind))
	// ErrInvalidStorageMode rejects a deploy whose storage mode is neither
	// "json" nor "table". The mode is fixed for the version's lifetime and
	// drives whether a dedicated physical form table is generated at publish,
	// so an unrecognized value must be caught when the version is created.
	ErrInvalidStorageMode = result.Err(i18n.T("approval_invalid_storage_mode"), result.WithCode(ErrCodeInvalidStorageMode))
	// ErrFlowBindingLocked is retained as a stable error surface for older
	// consumers. Version-pinned binding snapshots mean current flow commands no
	// longer return it when a mutable flow binding changes.
	ErrFlowBindingLocked = result.Err(i18n.T("approval_flow_binding_locked"), result.WithCode(ErrCodeFlowBindingLocked))
	// ErrBindingColumnsConflict rejects duplicate key/write-back columns, which
	// could otherwise mutate the lookup key or assign one column twice.
	ErrBindingColumnsConflict = result.Err(i18n.T("approval_binding_columns_conflict"), result.WithCode(ErrCodeBindingColumnsConflict))
	// ErrBindingUnexpected rejects business binding configuration on a
	// standalone flow.
	ErrBindingUnexpected = result.Err(i18n.T("approval_binding_unexpected"), result.WithCode(ErrCodeBindingUnexpected))
	// ErrBindingSchemaInvalid rejects a binding whose configured table or
	// columns do not exist in the primary database.
	ErrBindingSchemaInvalid = result.Err(i18n.T("approval_binding_schema_invalid"), result.WithCode(ErrCodeBindingSchemaInvalid))
	// ErrBindingKeyNotUnique rejects key columns that are not backed by one
	// complete, non-null primary or unique key.
	ErrBindingKeyNotUnique = result.Err(i18n.T("approval_binding_key_not_unique"), result.WithCode(ErrCodeBindingKeyNotUnique))
	// ErrBindingStatusMappingInvalid rejects unknown approval statuses and blank
	// target values in a business status mapping.
	ErrBindingStatusMappingInvalid = result.Err(
		i18n.T("approval_binding_status_mapping_invalid"),
		result.WithCode(ErrCodeBindingStatusMappingInvalid),
	)

	ErrInstanceNotFound          = result.Err(i18n.T("approval_instance_not_found"), result.WithCode(ErrCodeInstanceNotFound))
	ErrInstanceCompleted         = result.Err(i18n.T("approval_instance_completed"), result.WithCode(ErrCodeInstanceCompleted))
	ErrNotAllowedInitiate        = result.Err(i18n.T("approval_not_allowed_initiate"), result.WithCode(ErrCodeNotAllowedInitiate))
	ErrWithdrawNotAllowed        = result.Err(i18n.T("approval_withdraw_not_allowed"), result.WithCode(ErrCodeWithdrawNotAllowed))
	ErrResubmitNotAllowed        = result.Err(i18n.T("approval_resubmit_not_allowed"), result.WithCode(ErrCodeResubmitNotAllowed))
	ErrInvalidInstanceTransition = result.Err(i18n.T("approval_invalid_instance_transition"), result.WithCode(ErrCodeInvalidInstanceTransition))
	ErrBusinessRefRequired       = result.Err(i18n.T("approval_business_ref_required"), result.WithCode(ErrCodeBusinessRefRequired))
	ErrBindingTargetBusy         = result.Err(i18n.T("approval_binding_target_busy"), result.WithCode(ErrCodeBindingTargetBusy))
	ErrInvalidBusinessRef        = result.Err(i18n.T("approval_invalid_business_ref"), result.WithCode(ErrCodeInvalidBusinessRef))
	ErrBindingProjectionNotFound = result.Err(
		i18n.T("approval_binding_projection_not_found"),
		result.WithCode(ErrCodeBindingProjectionNotFound),
	)

	ErrTaskNotFound             = result.Err(i18n.T("approval_task_not_found"), result.WithCode(ErrCodeTaskNotFound))
	ErrTaskNotPending           = result.Err(i18n.T("approval_task_not_pending"), result.WithCode(ErrCodeTaskNotPending))
	ErrNotAssignee              = result.Err(i18n.T("approval_not_assignee"), result.WithCode(ErrCodeNotAssignee))
	ErrInvalidTaskTransition    = result.Err(i18n.T("approval_invalid_task_transition"), result.WithCode(ErrCodeInvalidTaskTransition))
	ErrRollbackNotAllowed       = result.Err(i18n.T("approval_rollback_not_allowed"), result.WithCode(ErrCodeRollbackNotAllowed))
	ErrAddAssigneeNotAllowed    = result.Err(i18n.T("approval_add_assignee_not_allowed"), result.WithCode(ErrCodeAddAssigneeNotAllowed))
	ErrTransferNotAllowed       = result.Err(i18n.T("approval_transfer_not_allowed"), result.WithCode(ErrCodeTransferNotAllowed))
	ErrOpinionRequired          = result.Err(i18n.T("approval_opinion_required"), result.WithCode(ErrCodeOpinionRequired))
	ErrManualCcNotAllowed       = result.Err(i18n.T("approval_manual_cc_not_allowed"), result.WithCode(ErrCodeManualCcNotAllowed))
	ErrRemoveAssigneeNotAllowed = result.Err(i18n.T("approval_remove_assignee_not_allowed"), result.WithCode(ErrCodeRemoveAssigneeNotAllowed))
	ErrInvalidAddAssigneeType   = result.Err(i18n.T("approval_invalid_add_assignee_type"), result.WithCode(ErrCodeInvalidAddAssigneeType))
	ErrNotApplicant             = result.Err(i18n.T("approval_not_applicant"), result.WithCode(ErrCodeNotApplicant))
	ErrInvalidRollbackTarget    = result.Err(i18n.T("approval_invalid_rollback_target"), result.WithCode(ErrCodeInvalidRollbackTarget))
	ErrLastAssigneeRemoval      = result.Err(i18n.T("approval_last_assignee_removal"), result.WithCode(ErrCodeLastAssigneeRemoval))
	ErrInvalidTransferTarget    = result.Err(i18n.T("approval_invalid_transfer_target"), result.WithCode(ErrCodeInvalidTransferTarget))
	ErrNoUsersSpecified         = result.Err(i18n.T("approval_no_users_specified"), result.WithCode(ErrCodeNoUsersSpecified))

	ErrNoAssignee            = result.Err(i18n.T("approval_no_assignee"), result.WithCode(ErrCodeNoAssignee))
	ErrAssigneeResolveFailed = result.Err(i18n.T("approval_assignee_resolve_failed"), result.WithCode(ErrCodeAssigneeResolveFailed))

	ErrFormValidationFailed = result.Err(i18n.T("approval_form_validation_failed"), result.WithCode(ErrCodeFormValidationFailed))
	// ErrFormDataTooLarge rejects submissions whose JSON-encoded form data
	// would exceed FormDataMaxBytes. Stops malicious clients from blowing
	// up the JSONB column or driving the runtime into OOM via deeply
	// nested or massive maps.
	ErrFormDataTooLarge = result.Err(
		i18n.T("approval_form_data_too_large"),
		result.WithCode(ErrCodeFormValidationFailed),
	)

	ErrAccessDenied = result.Err(i18n.T("approval_access_denied"), result.WithCode(ErrCodeAccessDenied))
	// ErrTerminateNotAllowed rejects force-closing an instance whose status
	// has no terminate transition on the instance state machine (already in
	// a final status).
	ErrTerminateNotAllowed = result.Err(i18n.T("approval_terminate_not_allowed"), result.WithCode(ErrCodeTerminateNotAllowed))
)

Error definitions. Messages are resolved through i18n at package init time using the language selected by VEF_I18N_LANGUAGE.

These are sentinel values — callers use errors.Is to recognize them, so the Error value must remain stable. Switching i18n language at runtime (e.g. via i18n.SetLanguage in tests) will not update these frozen messages; new translations only take effect on process restart.

View Source
var SystemOperator = approval.UserInfo{ID: "system", Name: "系统"}

SystemOperator is the operator identity stamped on actions the engine performs without a human decision: timeout auto-processing, auto-pass execution types, consecutive-approver passes, and similar. Sharing one identity keeps audit trails queryable by a single well-known operator ID.

Functions

func CollectUniqueCCUserIDs

func CollectUniqueCCUserIDs(
	ctx context.Context,
	configs []approval.FlowNodeCC,
	formData approval.FormData,
	resolver CCUserResolver,
	selector CCConfigSelector,
) []string

CollectUniqueCCUserIDs resolves and deduplicates CC user IDs while preserving first-seen order. It is the best-effort boundary for CC resolution: a config that cannot be resolved (missing AssigneeService, transient org-lookup error, unexpected form-field value, or unknown kind) is logged and skipped rather than failing the approval that triggered the CC — a notification side-effect must never roll back the business decision.

func ComputeTaskDeadline

func ComputeTaskDeadline(timeoutHours int) *timex.DateTime

ComputeTaskDeadline calculates a task deadline from timeout hours. Returns nil when timeout is disabled.

func HasUnreadCCRecords added in v0.29.0

func HasUnreadCCRecords(ctx context.Context, db orm.DB, instanceID, nodeID, visitID string) (bool, error)

HasUnreadCCRecords reports whether the CC node still has any record awaiting a read confirmation. It is the single source of truth for read-confirm CC node completion: both node entry (engine.CCProcessor deciding wait vs. continue) and the mark-read path (NodeService.AdvanceCCNodeIfAllRead deciding whether to advance) consult it, so the two can never disagree about whether the node is done. A node that resolved to zero recipients has no records and is therefore already complete — it must not wait, or nothing could ever advance it.

func InsertAutoCCRecords

func InsertAutoCCRecords(ctx context.Context, db orm.DB, instanceID, nodeID, visitID string, userIDs []string, userInfos map[string]approval.UserInfo) ([]string, error)

InsertAutoCCRecords inserts non-manual CC records and returns newly inserted IDs.

func InsertCCRecords

func InsertCCRecords(
	ctx context.Context,
	db orm.DB,
	instanceID string,
	nodeID *string,
	visitID *string,
	userIDs []string,
	userInfos map[string]approval.UserInfo,
	isManual bool,
) ([]string, error)

InsertCCRecords inserts CC records for the given users and returns only the newly inserted user IDs (existing records are ignored). Each record snapshots the recipient's display info — name and department — as resolved at send time.

Callers must hold an instance-level FOR UPDATE lock to prevent concurrent inserts from racing on the existence check.

nodeID and visitID are set together: a node-anchored record always belongs to one traversal, so dedup is visit-scoped — a rollback redo notifies (and waits) again. Instance-level records (both nil) dedup across the lifetime.

func InsertManualCCRecords

func InsertManualCCRecords(ctx context.Context, db orm.DB, instanceID, nodeID, visitID string, userIDs []string, userInfos map[string]approval.UserInfo) ([]string, error)

InsertManualCCRecords inserts manual CC records and returns newly inserted IDs.

func NormalizeUniqueIDs

func NormalizeUniqueIDs(ids []string) []string

NormalizeUniqueIDs trims, deduplicates, and filters empty IDs while preserving first-seen order.

func ResolveCCUserIDs

func ResolveCCUserIDs(cfg approval.FlowNodeCC, formData approval.FormData) ([]string, error)

ResolveCCUserIDs resolves CC recipients from static user IDs or form-field values. Role and department kinds are organizational lookups handled by CCRecipientResolver, not here — this function only covers the kinds that need no external service.

func ResolveUserInfo added in v0.35.0

func ResolveUserInfo(ctx context.Context, resolver approval.UserInfoResolver, userID string) approval.UserInfo

ResolveUserInfo resolves a single user ID to its display info. Returns a zero UserInfo on failure (best-effort for display-only fields); the ID field is always populated so callers can snapshot it verbatim.

func ResolveUserInfoMap added in v0.35.0

func ResolveUserInfoMap(ctx context.Context, resolver approval.UserInfoResolver, ids []string) (map[string]approval.UserInfo, error)

ResolveUserInfoMap batch-resolves user IDs to a map of ID→UserInfo (name plus optional department, per the host resolver). Missing IDs are simply absent — indexing the map yields a zero UserInfo whose fields are empty. Returns an error if the resolver fails.

func ResolveUserInfoMapSilent added in v0.35.0

func ResolveUserInfoMapSilent(ctx context.Context, resolver approval.UserInfoResolver, ids []string) map[string]approval.UserInfo

ResolveUserInfoMapSilent batch-resolves user IDs to a map of ID→UserInfo. Silently returns an empty map on resolver failure (best-effort for display-only fields).

func ToFloat64 added in v0.29.0

func ToFloat64(value any) (float64, bool)

ToFloat64 normalizes any JSON-decoded or Go-native numeric value to float64. It is the shared numeric bridge for form-data handling: condition evaluation and form validation both compare user-supplied numbers, and both must accept the full spread of types a decoder or caller may produce.

func UserHasRole added in v0.29.0

func UserHasRole(ctx context.Context, svc approval.AssigneeService, userID, roleID string) (bool, error)

UserHasRole reports whether the user currently holds the role. It is the single source of truth for role-membership checks: it prefers the host's direct RoleMembershipChecker capability and falls back to listing the role's members (correct for any host, but linear in role size). Routing every caller through it keeps the read and validation paths from answering the same question two different ways. A nil service reports no membership.

func UserInfos added in v0.35.0

func UserInfos(ids []string, infos map[string]approval.UserInfo) []approval.UserInfo

UserInfos builds the ordered person list for the given IDs from a resolved info map. An ID missing from the map still yields an entry carrying the ID, so unresolvable users stay visible in the record.

Types

type CCConfigSelector

type CCConfigSelector func(cfg approval.FlowNodeCC) bool

CCConfigSelector decides whether a FlowNodeCC config should be included.

type CCRecipientResolver added in v0.29.0

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

CCRecipientResolver resolves CC recipients for every CC kind. User and form-field kinds resolve from the config / form directly; role and department kinds resolve through the host AssigneeService, mirroring how assignees of the same kinds are resolved. This keeps CC symmetric with assignees instead of silently dropping role/department recipients.

func NewCCRecipientResolver added in v0.29.0

func NewCCRecipientResolver(assigneeSvc approval.AssigneeService) *CCRecipientResolver

NewCCRecipientResolver creates a CCRecipientResolver. assigneeSvc may be nil when the host registers no organizational service; role and department CC configs then surface an error that the best-effort CollectUniqueCCUserIDs boundary logs and skips, rather than failing the approval.

func (*CCRecipientResolver) Resolve added in v0.29.0

func (r *CCRecipientResolver) Resolve(ctx context.Context, cfg approval.FlowNodeCC, formData approval.FormData) ([]string, error)

Resolve resolves a single CC configuration to user IDs. Its signature satisfies CCUserResolver so it can be handed to CollectUniqueCCUserIDs, which owns the best-effort policy: this method reports resolution failures honestly (missing AssigneeService, org-lookup error), and the boundary logs and skips them so a CC notification never rolls back the approval that triggered it.

type CCUserResolver

type CCUserResolver func(ctx context.Context, cfg approval.FlowNodeCC, formData approval.FormData) ([]string, error)

CCUserResolver resolves CC user IDs from a single FlowNodeCC configuration. The context is threaded through for kinds (role / department) that resolve recipients via the host AssigneeService.

type CreateFlowInitiatorCmd

type CreateFlowInitiatorCmd struct {
	Kind approval.InitiatorKind
	IDs  []string
}

CreateFlowInitiatorCmd contains the parameters for creating a flow initiator.

type FlowGraph

type FlowGraph struct {
	Flow    *approval.Flow        `json:"flow"`
	Version *approval.FlowVersion `json:"version"`
	Nodes   []approval.FlowNode   `json:"nodes"`
	Edges   []approval.FlowEdge   `json:"edges"`
}

FlowGraph contains the complete flow graph for a version.

type OrderedUnique

type OrderedUnique[T comparable] struct {
	// contains filtered or unexported fields
}

OrderedUnique stores unique values while preserving first-seen order.

func NewOrderedUnique

func NewOrderedUnique[T comparable](capacity int) *OrderedUnique[T]

NewOrderedUnique creates an ordered-unique container with optional capacity.

func (*OrderedUnique[T]) Add

func (o *OrderedUnique[T]) Add(value T) bool

Add inserts value only if it does not already exist, preserving insertion order.

func (*OrderedUnique[T]) AddAll

func (o *OrderedUnique[T]) AddAll(values ...T) int

AddAll inserts multiple values and returns how many were newly added.

func (*OrderedUnique[T]) Contains

func (o *OrderedUnique[T]) Contains(value T) bool

Contains reports whether value already exists in the set.

func (*OrderedUnique[T]) Len

func (o *OrderedUnique[T]) Len() int

Len returns the number of unique values.

func (*OrderedUnique[T]) ToSlice

func (o *OrderedUnique[T]) ToSlice() []T

ToSlice returns a copy of ordered unique values.

Jump to

Keyboard shortcuts

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