package main import ( "context" "database/sql" "fmt" "iter" "os" "strings" "sync" _ "embed" _ "github.com/mattn/go-sqlite3" "github.com/uptrace/opentelemetry-go-extra/otelsql" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" "go.sour.is/xt/internal/otel" "go.yarn.social/lextwt" "go.yarn.social/types" "golang.org/x/sync/errgroup" ) func run(c *console) error { ctx, span := otel.Span(c.Context) defer span.End() a := c.Args() app := &appState{ args: a, feeds: sync.Map{}, queue: FibHeap(func(a, b *Feed) bool { return a.LastScanOn.Time.Before(b.LastScanOn.Time) }), } // Setup DB err := func(ctx context.Context) error { ctx, span := otel.Span(ctx) defer span.End() db, err := app.DB() if err != nil { return err } defer db.Close() for _, stmt := range strings.Split(initSQL, ";") { _, err = db.ExecContext(ctx, stmt) if err != nil { return err } } return nil }(ctx) if err != nil { return err } // Seed File err = func(ctx context.Context) error { ctx, span := otel.Span(ctx) defer span.End() f, err := os.Open(a.baseFeed) if err != nil { return err } defer f.Close() twtfile, err := lextwt.ParseFile(f, &types.Twter{ Nick: a.Nick, URI: a.URI, }) if err != nil { return fmt.Errorf("%w: %w", ErrParseFailed, err) } db, err := app.DB() if err != nil { return err } defer db.Close() return storeFeed(ctx, db, twtfile) }(ctx) if err != nil { return err } wg, ctx := errgroup.WithContext(ctx) c.Context = ctx wg.Go(func() error { return refreshLoop(c, app) }) go httpServer(c, app) wg.Wait() return c.Context.Err() } type appState struct { args args feeds sync.Map queue *fibHeap[Feed] } func (app *appState) DB() (*sql.DB, error) { // return sql.Open(app.args.dbtype, app.args.dbfile) return otelsql.Open(app.args.dbtype, app.args.dbfile, otelsql.WithAttributes(semconv.DBSystemSqlite), otelsql.WithDBName("mydb")) } func (app *appState) Feed(feedID string) *Feed { return nil } func (app *appState) Feeds() iter.Seq2[string, *Feed] { return func(yield func(string, *Feed) bool) { app.feeds.Range(func(k, v any) bool { key, _ := k.(string) value, ok := v.(*Feed) if !ok { return false } if !yield(key, value) { return false } return true }) } }