Line data Source code
1 : part of flutter_data;
2 :
3 : typedef FutureFn<R> = FutureOr<R> Function();
4 :
5 : class DataHelpers {
6 36 : static final uuid = Uuid();
7 :
8 36 : static final _types = <Object, String>{};
9 :
10 12 : static String getType<T>([String? type]) {
11 12 : if (T == dynamic && type == null) {
12 1 : throw UnsupportedError('Please supply a type');
13 : }
14 24 : if (!_types.containsKey(type ?? T)) {
15 12 : final newType = type ?? T.toString();
16 48 : _types[type ?? T] = newType.decapitalize().pluralize();
17 : }
18 24 : return _types[type ?? T]!;
19 : }
20 :
21 10 : static String generateShortKey<T>() {
22 30 : return uuid.v1().substring(0, 6);
23 : }
24 :
25 12 : static String generateKey<T>([String? type]) {
26 12 : type = getType<T>(type);
27 48 : return uuid.v1().substring(0, 8).typifyWith(type);
28 : }
29 : }
30 :
31 : class OfflineException extends DataException {
32 4 : OfflineException({required Object error}) : super(error);
33 1 : @override
34 : String toString() {
35 2 : return 'OfflineException: $error';
36 : }
37 : }
38 :
39 : abstract class _Lifecycle {
40 : @protected
41 : @visibleForTesting
42 : bool get isInitialized;
43 :
44 : void dispose();
45 : }
46 :
47 : class InternalHolder<T extends DataModel<T>> {
48 : final Map<String, dynamic> finders;
49 11 : InternalHolder(this.finders);
50 : }
51 :
52 : // finders
53 :
54 : class DataFinder {
55 12 : const DataFinder();
56 : }
57 :
58 : typedef DataFinderAll<T extends DataModel<T>> = Future<List<T>?> Function({
59 : bool? remote,
60 : bool? background,
61 : Map<String, dynamic>? params,
62 : Map<String, String>? headers,
63 : bool? syncLocal,
64 : OnSuccessAll<T>? onSuccess,
65 : OnErrorAll<T>? onError,
66 : DataRequestLabel? label,
67 : });
68 :
69 : typedef DataFinderOne<T extends DataModel<T>> = Future<T?> Function(
70 : Object model, {
71 : bool? remote,
72 : bool? background,
73 : Map<String, dynamic>? params,
74 : Map<String, String>? headers,
75 : OnSuccessOne<T>? onSuccess,
76 : OnErrorOne<T>? onError,
77 : DataRequestLabel? label,
78 : });
79 :
80 : typedef DataWatcherAll<T extends DataModel<T>> = DataStateNotifier<List<T>?>
81 : Function({
82 : bool? remote,
83 : Map<String, dynamic>? params,
84 : Map<String, String>? headers,
85 : bool? syncLocal,
86 : String? finder,
87 : DataRequestLabel? label,
88 : });
89 :
90 : typedef DataWatcherOne<T extends DataModel<T>> = DataStateNotifier<T?> Function(
91 : Object model, {
92 : bool? remote,
93 : Map<String, dynamic>? params,
94 : Map<String, String>? headers,
95 : AlsoWatch<T>? alsoWatch,
96 : String? finder,
97 : DataRequestLabel? label,
98 : });
99 :
100 : // watch
101 :
102 : typedef Watcher = W Function<W>(ProviderListenable<W> provider);
103 :
104 : // relationships + alsoWatch
105 :
106 : class RelationshipGraphNode<T extends DataModel<T>> {}
107 :
108 : class RelationshipMeta<T extends DataModel<T>>
109 : with RelationshipGraphNode<T>, EquatableMixin {
110 : final String name;
111 : final String? inverseName;
112 : final String type;
113 : final String kind;
114 : final bool serialize;
115 : final Relationship? Function(DataModel) instance;
116 : RelationshipMeta? parent;
117 : RelationshipMeta? child;
118 :
119 11 : RelationshipMeta({
120 : required this.name,
121 : this.inverseName,
122 : required this.type,
123 : required this.kind,
124 : this.serialize = true,
125 : required this.instance,
126 : });
127 :
128 : // get topmost parent
129 1 : RelationshipMeta get _top {
130 : RelationshipMeta? current = this;
131 1 : while (current?.parent != null) {
132 1 : current = current!.parent;
133 : }
134 : return current!;
135 : }
136 :
137 1 : RelationshipMeta<T> clone({RelationshipMeta? parent}) {
138 1 : final meta = RelationshipMeta<T>(
139 1 : name: name,
140 1 : type: type,
141 1 : kind: kind,
142 1 : instance: instance,
143 : );
144 : if (parent != null) {
145 1 : meta.parent = parent;
146 2 : meta.parent!.child = meta; // automatically set child
147 : }
148 : return meta;
149 : }
150 :
151 1 : @override
152 6 : List<Object?> get props => [name, inverseName, type, kind, serialize];
153 : }
154 :
155 : typedef AlsoWatch<T extends DataModel<T>> = Iterable<RelationshipGraphNode>
156 : Function(RelationshipGraphNode<T>);
157 :
158 : /// This argument holder class is used internally with
159 : /// Riverpod `family`s.
160 : class WatchArgs<T extends DataModel<T>> with EquatableMixin {
161 4 : WatchArgs({
162 : this.key,
163 : this.remote,
164 : this.params,
165 : this.headers,
166 : this.syncLocal,
167 : this.relationshipMetas,
168 : this.alsoWatch,
169 : this.finder,
170 : this.label,
171 : });
172 :
173 : final String? key;
174 : final bool? remote;
175 : final Map<String, dynamic>? params;
176 : final Map<String, String>? headers;
177 : final bool? syncLocal;
178 : final List<RelationshipMeta>? relationshipMetas;
179 : final AlsoWatch<T>? alsoWatch;
180 : final String? finder;
181 : final DataRequestLabel? label;
182 :
183 4 : @override
184 4 : List<Object?> get props => [
185 4 : key,
186 4 : remote,
187 4 : params,
188 4 : headers,
189 4 : syncLocal,
190 4 : relationshipMetas,
191 4 : finder,
192 4 : label
193 : ];
194 : }
195 :
196 : // ignore: constant_identifier_names
197 20 : enum DataRequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }
198 :
199 : extension _ToStringX on DataRequestMethod {
200 24 : String toShortString() => toString().split('.').last;
201 : }
202 :
203 : typedef _OnSuccessGeneric<R> = FutureOr<R?> Function(
204 : Object? data, DataRequestLabel label);
205 : typedef OnSuccessOne<T extends DataModel<T>> = FutureOr<T?> Function(
206 : Object? data, DataRequestLabel label, RemoteAdapter<T> adapter);
207 : typedef OnSuccessAll<T extends DataModel<T>> = FutureOr<List<T>?> Function(
208 : Object? data, DataRequestLabel label, RemoteAdapter<T> adapter);
209 :
210 : typedef _OnErrorGeneric<R> = FutureOr<R?> Function(
211 : DataException e, DataRequestLabel label);
212 : typedef OnErrorOne<T extends DataModel<T>> = FutureOr<T?> Function(
213 : DataException e, DataRequestLabel label, RemoteAdapter<T> adapter);
214 : typedef OnErrorAll<T extends DataModel<T>> = FutureOr<List<T>?> Function(
215 : DataException e, DataRequestLabel label, RemoteAdapter<T> adapter);
216 :
217 : /// Data request information holder.
218 : ///
219 : /// Format examples:
220 : /// - findAll/reports@b5d14c
221 : /// - findOne/inspections#3@c4a1bb
222 : /// - findAll/reports@b5d14c<c4a1bb
223 : class DataRequestLabel with EquatableMixin {
224 : final String kind;
225 : late final String type;
226 : final String? id;
227 : DataModel? model;
228 : final timestamp = DateTime.now();
229 : final _requestIds = <String>[];
230 :
231 3 : String get requestId => _requestIds.first;
232 8 : int get indentation => _requestIds.length - 1;
233 :
234 10 : DataRequestLabel(
235 : String kind, {
236 : required String type,
237 : this.id,
238 : String? requestId,
239 : this.model,
240 : DataRequestLabel? withParent,
241 10 : }) : kind = kind.trim() {
242 20 : assert(!type.contains('#'));
243 10 : if (id != null) {
244 30 : assert(!id!.contains('#'));
245 : }
246 : if (requestId != null) {
247 4 : assert(!requestId.contains('@'));
248 : }
249 20 : this.type = DataHelpers.getType(type);
250 30 : _requestIds.add(requestId ?? DataHelpers.generateShortKey());
251 :
252 : if (withParent != null) {
253 12 : _requestIds.addAll(withParent._requestIds);
254 : }
255 : }
256 :
257 2 : factory DataRequestLabel.parse(String text) {
258 2 : final parts = text.split('/');
259 4 : final parts2 = parts.last.split('@');
260 4 : final parts3 = parts2[0].split('#');
261 4 : final kind = (parts..removeLast()).join('/');
262 2 : final requestId = parts2[1];
263 2 : final type = parts3[0];
264 6 : final id = parts3.length > 1 ? parts3[1] : null;
265 :
266 2 : return DataRequestLabel(kind, type: type, id: id, requestId: requestId);
267 : }
268 :
269 6 : @override
270 : String toString() {
271 42 : return '$kind/${(id ?? '').typifyWith(type)}@${_requestIds.join('<')}';
272 : }
273 :
274 1 : @override
275 5 : List<Object?> get props => [kind, type, id, _requestIds];
276 : }
277 :
278 : /// ONLY FOR FLUTTER DATA INTERNAL USE
279 33 : final internalRepositories = <String, Repository>{};
|