A vibe coded tangled fork which supports pijul.
at sl/tap-appview 358 lines 7.3 kB view raw
1package db 2 3import ( 4 "database/sql" 5 "fmt" 6 "maps" 7 "slices" 8 "strings" 9 "time" 10 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/orm" 14) 15 16func UpsertLabelDefinition(e Execer, l *models.LabelDefinition) (int64, error) { 17 panic("unimplemented") 18} 19 20// no updating type for now 21func AddLabelDefinition(e Execer, l *models.LabelDefinition) (int64, error) { 22 result, err := e.Exec( 23 `insert into label_definitions ( 24 did, 25 rkey, 26 name, 27 value_type, 28 value_format, 29 value_enum, 30 scope, 31 color, 32 multiple, 33 created 34 ) 35 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 36 on conflict(did, rkey) do update set 37 name = excluded.name, 38 scope = excluded.scope, 39 color = excluded.color, 40 multiple = excluded.multiple`, 41 l.Did, 42 l.Rkey, 43 l.Name, 44 l.ValueType.Type, 45 l.ValueType.Format, 46 strings.Join(l.ValueType.Enum, ","), 47 strings.Join(l.Scope, ","), 48 l.Color, 49 l.Multiple, 50 l.Created.Format(time.RFC3339), 51 time.Now().Format(time.RFC3339), 52 ) 53 if err != nil { 54 return 0, err 55 } 56 57 id, err := result.LastInsertId() 58 if err != nil { 59 return 0, err 60 } 61 62 l.Id = id 63 64 return id, nil 65} 66 67func DeleteLabelDefinition(e Execer, filters ...orm.Filter) error { 68 var conditions []string 69 var args []any 70 for _, filter := range filters { 71 conditions = append(conditions, filter.Condition()) 72 args = append(args, filter.Arg()...) 73 } 74 whereClause := "" 75 if conditions != nil { 76 whereClause = " where " + strings.Join(conditions, " and ") 77 } 78 query := fmt.Sprintf(`delete from label_definitions %s`, whereClause) 79 _, err := e.Exec(query, args...) 80 return err 81} 82 83func GetLabelDefinitions(e Execer, filters ...orm.Filter) ([]models.LabelDefinition, error) { 84 var labelDefinitions []models.LabelDefinition 85 var conditions []string 86 var args []any 87 88 for _, filter := range filters { 89 conditions = append(conditions, filter.Condition()) 90 args = append(args, filter.Arg()...) 91 } 92 93 whereClause := "" 94 if conditions != nil { 95 whereClause = " where " + strings.Join(conditions, " and ") 96 } 97 98 query := fmt.Sprintf( 99 ` 100 select 101 id, 102 did, 103 rkey, 104 name, 105 value_type, 106 value_format, 107 value_enum, 108 scope, 109 color, 110 multiple, 111 created 112 from label_definitions 113 %s 114 order by created 115 `, 116 whereClause, 117 ) 118 119 rows, err := e.Query(query, args...) 120 if err != nil { 121 return nil, err 122 } 123 defer rows.Close() 124 125 for rows.Next() { 126 var labelDefinition models.LabelDefinition 127 var createdAt, enumVariants, scopes string 128 var color sql.Null[string] 129 var multiple int 130 131 if err := rows.Scan( 132 &labelDefinition.Id, 133 &labelDefinition.Did, 134 &labelDefinition.Rkey, 135 &labelDefinition.Name, 136 &labelDefinition.ValueType.Type, 137 &labelDefinition.ValueType.Format, 138 &enumVariants, 139 &scopes, 140 &color, 141 &multiple, 142 &createdAt, 143 ); err != nil { 144 return nil, err 145 } 146 147 labelDefinition.Created, err = time.Parse(time.RFC3339, createdAt) 148 if err != nil { 149 labelDefinition.Created = time.Now() 150 } 151 152 if color.Valid { 153 labelDefinition.Color = &color.V 154 } 155 156 if multiple != 0 { 157 labelDefinition.Multiple = true 158 } 159 160 if enumVariants != "" { 161 labelDefinition.ValueType.Enum = strings.Split(enumVariants, ",") 162 } 163 164 for s := range strings.SplitSeq(scopes, ",") { 165 labelDefinition.Scope = append(labelDefinition.Scope, s) 166 } 167 168 labelDefinitions = append(labelDefinitions, labelDefinition) 169 } 170 171 return labelDefinitions, nil 172} 173 174// helper to get exactly one label def 175func GetLabelDefinition(e Execer, filters ...orm.Filter) (*models.LabelDefinition, error) { 176 labels, err := GetLabelDefinitions(e, filters...) 177 if err != nil { 178 return nil, err 179 } 180 181 if labels == nil { 182 return nil, sql.ErrNoRows 183 } 184 185 if len(labels) != 1 { 186 return nil, fmt.Errorf("too many rows returned") 187 } 188 189 return &labels[0], nil 190} 191 192func AddLabelOp(e Execer, l *models.LabelOp) (int64, error) { 193 now := time.Now() 194 result, err := e.Exec( 195 `insert into label_ops ( 196 did, 197 rkey, 198 subject, 199 operation, 200 operand_key, 201 operand_value, 202 performed, 203 indexed 204 ) 205 values (?, ?, ?, ?, ?, ?, ?, ?) 206 on conflict(did, rkey, subject, operand_key, operand_value) do update set 207 operation = excluded.operation, 208 operand_value = excluded.operand_value, 209 performed = excluded.performed, 210 indexed = excluded.indexed`, 211 l.Did, 212 l.Rkey, 213 l.Subject.String(), 214 string(l.Operation), 215 l.OperandKey, 216 l.OperandValue, 217 l.PerformedAt.Format(time.RFC3339), 218 now.Format(time.RFC3339), 219 ) 220 if err != nil { 221 return 0, err 222 } 223 224 id, err := result.LastInsertId() 225 if err != nil { 226 return 0, err 227 } 228 229 l.Id = id 230 l.IndexedAt = now 231 232 return id, nil 233} 234 235func GetLabelOps(e Execer, filters ...orm.Filter) ([]models.LabelOp, error) { 236 var labelOps []models.LabelOp 237 var conditions []string 238 var args []any 239 240 for _, filter := range filters { 241 conditions = append(conditions, filter.Condition()) 242 args = append(args, filter.Arg()...) 243 } 244 245 whereClause := "" 246 if conditions != nil { 247 whereClause = " where " + strings.Join(conditions, " and ") 248 } 249 250 query := fmt.Sprintf( 251 ` 252 select 253 id, 254 did, 255 rkey, 256 subject, 257 operation, 258 operand_key, 259 operand_value, 260 performed, 261 indexed 262 from label_ops 263 %s 264 order by indexed 265 `, 266 whereClause, 267 ) 268 269 rows, err := e.Query(query, args...) 270 if err != nil { 271 return nil, err 272 } 273 defer rows.Close() 274 275 for rows.Next() { 276 var labelOp models.LabelOp 277 var performedAt, indexedAt string 278 279 if err := rows.Scan( 280 &labelOp.Id, 281 &labelOp.Did, 282 &labelOp.Rkey, 283 &labelOp.Subject, 284 &labelOp.Operation, 285 &labelOp.OperandKey, 286 &labelOp.OperandValue, 287 &performedAt, 288 &indexedAt, 289 ); err != nil { 290 return nil, err 291 } 292 293 labelOp.PerformedAt, err = time.Parse(time.RFC3339, performedAt) 294 if err != nil { 295 labelOp.PerformedAt = time.Now() 296 } 297 298 labelOp.IndexedAt, err = time.Parse(time.RFC3339, indexedAt) 299 if err != nil { 300 labelOp.IndexedAt = time.Now() 301 } 302 303 labelOps = append(labelOps, labelOp) 304 } 305 306 return labelOps, nil 307} 308 309// get labels for a given list of subject URIs 310func GetLabels(e Execer, filters ...orm.Filter) (map[syntax.ATURI]models.LabelState, error) { 311 ops, err := GetLabelOps(e, filters...) 312 if err != nil { 313 return nil, err 314 } 315 316 // group ops by subject 317 opsBySubject := make(map[syntax.ATURI][]models.LabelOp) 318 for _, op := range ops { 319 subject := syntax.ATURI(op.Subject) 320 opsBySubject[subject] = append(opsBySubject[subject], op) 321 } 322 323 // get all unique labelats for creating the context 324 labelAtSet := make(map[string]bool) 325 for _, op := range ops { 326 labelAtSet[op.OperandKey] = true 327 } 328 labelAts := slices.Collect(maps.Keys(labelAtSet)) 329 330 actx, err := NewLabelApplicationCtx(e, orm.FilterIn("at_uri", labelAts)) 331 if err != nil { 332 return nil, err 333 } 334 335 // apply label ops for each subject and collect results 336 results := make(map[syntax.ATURI]models.LabelState) 337 for subject, subjectOps := range opsBySubject { 338 state := models.NewLabelState() 339 actx.ApplyLabelOps(state, subjectOps) 340 results[subject] = state 341 } 342 343 return results, nil 344} 345 346func NewLabelApplicationCtx(e Execer, filters ...orm.Filter) (*models.LabelApplicationCtx, error) { 347 labels, err := GetLabelDefinitions(e, filters...) 348 if err != nil { 349 return nil, err 350 } 351 352 defs := make(map[string]*models.LabelDefinition) 353 for _, l := range labels { 354 defs[l.AtUri().String()] = &l 355 } 356 357 return &models.LabelApplicationCtx{Defs: defs}, nil 358}