Documentation
¶
Index ¶
- Constants
- Variables
- func CollectUniqueCCUserIDs(ctx context.Context, configs []approval.FlowNodeCC, formData approval.FormData, ...) []string
- func ComputeTaskDeadline(timeoutHours int) *timex.DateTime
- func HasUnreadCCRecords(ctx context.Context, db orm.DB, instanceID, nodeID, visitID string) (bool, error)
- func InsertAutoCCRecords(ctx context.Context, db orm.DB, instanceID, nodeID, visitID string, ...) ([]string, error)
- func InsertCCRecords(ctx context.Context, db orm.DB, instanceID string, nodeID *string, ...) ([]string, error)
- func InsertManualCCRecords(ctx context.Context, db orm.DB, instanceID, nodeID, visitID string, ...) ([]string, error)
- func NormalizeUniqueIDs(ids []string) []string
- func ResolveCCUserIDs(cfg approval.FlowNodeCC, formData approval.FormData) ([]string, error)
- func ResolveUserInfo(ctx context.Context, resolver approval.UserInfoResolver, userID string) approval.UserInfo
- func ResolveUserInfoMap(ctx context.Context, resolver approval.UserInfoResolver, ids []string) (map[string]approval.UserInfo, error)
- func ResolveUserInfoMapSilent(ctx context.Context, resolver approval.UserInfoResolver, ids []string) map[string]approval.UserInfo
- func ToFloat64(value any) (float64, bool)
- func UserHasRole(ctx context.Context, svc approval.AssigneeService, userID, roleID string) (bool, error)
- func UserInfos(ids []string, infos map[string]approval.UserInfo) []approval.UserInfo
- type CCConfigSelector
- type CCRecipientResolver
- type CCUserResolver
- type CreateFlowInitiatorCmd
- type FlowGraph
- type OrderedUnique
Constants ¶
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).
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 ¶
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.
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 ¶
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 ¶
NormalizeUniqueIDs trims, deduplicates, and filters empty IDs while preserving first-seen order.
func ResolveCCUserIDs ¶
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
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.
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.