package schema import ( "errors" "reflect" "strings" "time" ) type Column struct { Name string FieldName string Type string Modifiers map[string]string } func NewColumn(name string, typ string, fieldName string, tag reflect.StructTag) Column { return Column{name, typ, fieldName, determineModifiers(tag)} } func determineModifiers(tag reflect.StructTag) map[string]string { modifiers := make(map[string]string) sqlTag := tag.Get("sql") if sqlTag == "" { return modifiers } rawModifiers := strings.SplitSeq(sqlTag, ";") for rawModifier := range rawModifiers { if strings.Contains(rawModifier, "=") { keyValue := strings.Split(rawModifier, "=") modifiers[keyValue[0]] = keyValue[1] } else { modifiers[rawModifier] = "" } } return modifiers } func (c *Column) GetDDL() (string, error) { var ddlBuilder strings.Builder ddlBuilder.WriteString(c.Name + " " + c.Type) inlineMods := c.inlineModifiers() for _, mod := range inlineMods { ddlBuilder.WriteString(" " + mod) } return ddlBuilder.String(), nil } func (c *Column) inlineModifiers() []string { var mods []string for k := range c.Modifiers { switch k { case "nn": mods = append(mods, "NOT NULL") } } return mods } func (c Column) GetSelectDML() (string, error) { return c.Name, nil } func (c Column) GetInsertDML() (string, error) { return c.Name, nil } func (c Column) GetUpdateDML() (string, error) { return c.Name + " = ?", nil } func (Column) GetDeleteDML() (string, error) { return "", errors.New("No delete DML for columns") } func (c *Column) IsPK() bool { _, ok := c.Modifiers["pk"] return ok } // Encodes the value to be DB compatible func (c Column) Encode(value reflect.Value) any { typ := value.Type().String() var encoded any switch typ { case "time.Time": timeVal := value.Interface().(time.Time) encoded = timeVal.UnixMilli() case "bool": boolVal := value.Interface().(bool) if boolVal { encoded = 1 } else { encoded = false } default: encoded = value.Interface() } return encoded } // Decodes the raw value from the DBs func (c Column) Decode(expected string, rawValue reflect.Value) any { var decoded any switch expected { case "time.Time": decoded = time.UnixMilli(rawValue.Interface().(int64)) case "bool": rawValue := rawValue.Interface().(int) if rawValue > 0 { decoded = true } else { decoded = false } default: decoded = rawValue.Interface() } return decoded }