trphoenix
2018-11-29 25f4612acc6885d3f977c16252e2185b874b3394
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
 
import 'package:analyzer/dart/element/type.dart';
import 'package:source_gen/source_gen.dart' show TypeChecker;
 
import '../constants.dart';
import '../shared_checkers.dart';
import '../type_helper.dart';
 
class IterableHelper extends TypeHelper<TypeHelperContextWithConfig> {
  const IterableHelper();
 
  @override
  String serialize(DartType targetType, String expression,
      TypeHelperContextWithConfig context) {
    if (!coreIterableTypeChecker.isAssignableFromType(targetType)) {
      return null;
    }
 
    final itemType = coreIterableGenericType(targetType);
 
    // This block will yield a regular list, which works fine for JSON
    // Although it's possible that child elements may be marked unsafe
 
    var isList = _coreListChecker.isAssignableFromType(targetType);
    final subField = context.serialize(itemType, closureArg);
 
    final optionalQuestion = context.nullable ? '?' : '';
 
    // In the case of trivial JSON types (int, String, etc), `subField`
    // will be identical to `substitute` – so no explicit mapping is needed.
    // If they are not equal, then we to write out the substitution.
    if (subField != closureArg) {
      final lambda = LambdaResult.process(subField, closureArg);
      if (context.config.useWrappers && isList) {
        var method = '\$wrapList';
        if (context.nullable) {
          method = '${method}HandleNull';
        }
 
        return '$method<$itemType>($expression, $lambda)';
      }
 
      expression = '$expression$optionalQuestion.map($lambda)';
 
      // expression now represents an Iterable (even if it started as a List
      // ...resetting `isList` to `false`.
      isList = false;
    }
 
    if (!isList) {
      // If the static type is not a List, generate one.
      expression += '$optionalQuestion.toList()';
    }
 
    return expression;
  }
 
  @override
  String deserialize(
      DartType targetType, String expression, TypeHelperContext context) {
    if (!coreIterableTypeChecker.isAssignableFromType(targetType)) {
      return null;
    }
 
    final iterableGenericType = coreIterableGenericType(targetType);
 
    final itemSubVal = context.deserialize(iterableGenericType, closureArg);
 
    // If `itemSubVal` is the same and it's not a Set, then we don't need to do
    // anything fancy
    if (closureArg == itemSubVal &&
        !_coreSetChecker.isAssignableFromType(targetType)) {
      return '$expression as List';
    }
 
    final optionalQuestion = context.nullable ? '?' : '';
 
    final lambda = LambdaResult.process(itemSubVal, closureArg);
 
    var output = '($expression as List)$optionalQuestion.map($lambda)';
 
    if (_coreListChecker.isAssignableFromType(targetType)) {
      output += '$optionalQuestion.toList()';
    } else if (_coreSetChecker.isAssignableFromType(targetType)) {
      output += '$optionalQuestion.toSet()';
    }
 
    return output;
  }
}
 
final _coreListChecker = const TypeChecker.fromUrl('dart:core#List');
final _coreSetChecker = const TypeChecker.fromUrl('dart:core#Set');