// Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package optional import ( "database/sql" "database/sql/driver" "reflect" "strconv" "xorm.io/xorm/schemas" ) type Option[T any] []T func None[T any]() Option[T] { return nil } func Some[T any](v T) Option[T] { return Option[T]{v} } func FromPtr[T any](v *T) Option[T] { if v == nil { return None[T]() } return Some(*v) } func FromNonDefault[T comparable](v T) Option[T] { var zero T if v == zero { return None[T]() } return Some(v) } func (o Option[T]) Has() bool { return o != nil } func (o Option[T]) Get() (has bool, value T) { if o != nil { has = true value = o[0] } return has, value } func (o Option[T]) ValueOrZeroValue() T { var zeroValue T return o.ValueOrDefault(zeroValue) } func (o Option[T]) ValueOrDefault(v T) T { if o.Has() { return o[0] } return v } // ParseBool get the corresponding optional.Option[bool] of a string using strconv.ParseBool func ParseBool(s string) Option[bool] { v, e := strconv.ParseBool(s) if e != nil { return None[bool]() } return Some(v) } // Option[T] can be used in an xorm bean as a field type for a nullable column. Multiple interfaces must be implemented // for this to work correctly and won't be checked at compile-time of the bean struct, so they're asserted here in case // the interface definitions change: var ( _ sql.Scanner = (*Option[bool])(nil) // read data from DB _ driver.Valuer = None[bool]() // write data to DB _ schemas.SQLTypeDelegator = None[bool]() // represent column field type correctly ) // Convert database data into an Option[T]. sql.Null[T] has all the necessary logic to perform Value(), so it is used as // an implementation. func (o *Option[T]) Scan(value any) error { var n sql.Null[T] if err := n.Scan(value); err != nil { return err } if n.Valid { *o = Some(n.V) } else { *o = None[T]() } return nil } // Convert Option[T] into the necessary database data to represent it. sql.Null[T] has all the necessary logic to // perform Value(), so it is used as an implementation. func (o Option[T]) Value() (driver.Value, error) { var n sql.Null[T] if o.Has() { n.V = o[0] n.Valid = true } else { n.Valid = false } return n.Value() } // Make xorm use whatever SQLType is appropriate for T to represent Option[T] in the database table func (o Option[T]) DelegateSQLType() reflect.Type { return reflect.TypeFor[T]() }