From 2db108529fd9f5cc04595ad5a31e1488d15df4d6 Mon Sep 17 00:00:00 2001 From: micimize Date: Fri, 2 Apr 2021 21:18:55 -0500 Subject: [PATCH] break Selectable methods into smaller interfaces based on return type. This makes it so users can expose more refined/foolproof apis to their application: ```dart extension TaskMethods on Task { /// won't make mistakes like using getSingle when using SingleOrNullSelectable get event => db.calendarEvents.forTask(this); /// autocomplete ignores getSingle, etc MultiSelectable get metrics => db.metrics.forTask(this); } ``` --- .../query_builder/statements/query.dart | 104 ++++++++++++------ 1 file changed, 69 insertions(+), 35 deletions(-) diff --git a/moor/lib/src/runtime/query_builder/statements/query.dart b/moor/lib/src/runtime/query_builder/statements/query.dart index ce576ee0..cc54500b 100644 --- a/moor/lib/src/runtime/query_builder/statements/query.dart +++ b/moor/lib/src/runtime/query_builder/statements/query.dart @@ -68,19 +68,22 @@ abstract class Query extends Component { } } -/// Abstract class for queries which can return one-time values or a stream -/// of values. -abstract class Selectable { +/// [Selectable] methods for returning multiple results. +abstract class MultiSelectable { /// Executes this statement and returns the result. Future> get(); /// Creates an auto-updating stream of the result that emits new items /// whenever any table used in this statement changes. Stream> watch(); +} - /// Executes this statement, like [get], but only returns one value. If the - /// query returns no or too many rows, the returned future will complete with - /// an error. +/// [Selectable] methods for returning or streaming single, +/// non-nullable results. +abstract class SingleSelectable { + /// Executes this statement, like [Selectable.get], but only returns one + /// value. the query returns no or too many rows, the returned future will + /// complete with an error. /// /// {@template moor_single_query_expl} /// Be aware that this operation won't put a limit clause on this statement, @@ -99,20 +102,69 @@ abstract class Selectable { /// clause will only allow one row. /// {@endtemplate} /// - /// See also: [getSingleOrNull], which returns `null` instead of throwing if - /// the query completes with no rows. + /// See also: [Selectable.getSingleOrNull], which returns `null` instead of + /// throwing if the query completes with no rows. + Future getSingle(); + + /// Creates an auto-updating stream of this statement, similar to + /// [Selectable.watch]. However, it is assumed that the query will only emit + /// one result, so instead of returning a `Stream>`, this returns a + /// `Stream`. If, at any point, the query emits no or more than one rows, + /// an error will be added to the stream instead. + /// + /// {@macro moor_single_query_expl} + Stream watchSingle(); +} + +/// [Selectable] methods for returning or streaming single, +/// nullable results. +abstract class SingleOrNullSelectable { + /// Executes this statement, like [Selectable.get], but only returns one + /// value. If the result too many values, this method will throw. If no + /// row is returned, `null` will be returned instead. + /// + /// {@macro moor_single_query_expl} + /// + /// See also: [Selectable.getSingle], which can be used if the query will + /// always evaluate to exactly one row. + Future getSingleOrNull(); + + /// Creates an auto-updating stream of this statement, similar to + /// [Selectable.watch]. However, it is assumed that the query will only + /// emit one result, so instead of returning a `Stream>`, this + /// returns a `Stream`. If the query emits more than one row at + /// some point, an error will be emitted to the stream instead. + /// If the query emits zero rows at some point, `null` will be added + /// to the stream instead. + /// + /// {@macro moor_single_query_expl} + Stream watchSingleOrNull(); +} + +/// Abstract class for queries which can return one-time values or a stream +/// of values. +abstract class Selectable + implements + MultiSelectable, + SingleSelectable, + SingleOrNullSelectable { + @override + Future> get(); + + @override + Stream> watch(); + + @override Future getSingle() async { return (await get()).single; } - /// Executes this statement, like [get], but only returns one value. If the - /// result too many values, this method will throw. If no row is returned, - /// `null` will be returned instead. - /// - /// {@macro moor_single_query_expl} - /// - /// See also: [getSingle], which can be used if the query will never evaluate - /// to exactly one row. + @override + Stream watchSingle() { + return watch().transform(singleElements()); + } + + @override Future getSingleOrNull() async { final list = await get(); final iterator = list.iterator; @@ -128,25 +180,7 @@ abstract class Selectable { return element; } - /// Creates an auto-updating stream of this statement, similar to [watch]. - /// However, it is assumed that the query will only emit one result, so - /// instead of returning a `Stream>`, this returns a `Stream`. If, - /// at any point, the query emits no or more than one rows, an error will be - /// added to the stream instead. - /// - /// {@macro moor_single_query_expl} - Stream watchSingle() { - return watch().transform(singleElements()); - } - - /// Creates an auto-updating stream of this statement, similar to [watch]. - /// However, it is assumed that the query will only emit one result, so - /// instead of returning a `Stream>`, this returns a `Stream`. If - /// the query emits more than one row at some point, an error will be emitted - /// to the stream instead. If the query emits zero rows at some point, `null` - /// will be added to the stream instead. - /// - /// {@macro moor_single_query_expl} + @override Stream watchSingleOrNull() { return watch().transform(singleElementsOrNull()); }