Line data Source code
1 : import 'dart:async'; 2 : import 'dart:collection'; 3 : import 'dart:convert'; 4 : 5 : import 'options.dart'; 6 : import 'parameter.dart'; 7 : 8 : /// A regular expression that matches strings that are composed entirely of 9 : /// ASCII-compatible characters. 10 3 : final RegExp _asciiOnly = RegExp(r'^[\x00-\x7F]+$'); 11 : 12 : /// Returns whether [string] is composed entirely of ASCII-compatible 13 : /// characters. 14 3 : bool isPlainAscii(String string) => _asciiOnly.hasMatch(string); 15 : 16 : /// Pipes all data and errors from [stream] into [sink]. Completes [Future] once 17 : /// [stream] is done. Unlike [store], [sink] remains open after [stream] is 18 : /// done. 19 1 : Future writeStreamToSink(Stream stream, EventSink sink) { 20 1 : var completer = Completer(); 21 2 : stream.listen(sink.add, 22 3 : onError: sink.addError, onDone: () => completer.complete()); 23 1 : return completer.future; 24 : } 25 : 26 : /// Returns the [Encoding] that corresponds to [charset]. Returns [fallback] if 27 : /// [charset] is null or if no [Encoding] was found that corresponds to 28 : /// [charset]. 29 1 : Encoding encodingForCharset(String? charset, [Encoding fallback = latin1]) { 30 : if (charset == null) return fallback; 31 0 : var encoding = Encoding.getByName(charset); 32 : return encoding ?? fallback; 33 : } 34 : 35 : typedef DioEncodeHandler = String? Function(String key, Object? value); 36 : 37 10 : String encodeMap( 38 : data, 39 : DioEncodeHandler handler, { 40 : bool encode = true, 41 : ListFormat listFormat = ListFormat.multi, 42 : }) { 43 10 : var urlData = StringBuffer(''); 44 : var first = true; 45 : var leftBracket = encode ? '%5B' : '['; 46 : var rightBracket = encode ? '%5D' : ']'; 47 1 : var encodeComponent = encode ? Uri.encodeQueryComponent : (e) => e; 48 10 : void urlEncode(dynamic sub, String path) { 49 : // detect if the list format for this parameter derivates from default 50 11 : final format = sub is ListParam ? sub.format : listFormat; 51 10 : final separatorChar = _getSeparatorChar(format); 52 : 53 10 : if (sub is ListParam) { 54 : // Need to unwrap all param objects here 55 1 : sub = sub.value; 56 : } 57 : 58 10 : if (sub is List) { 59 3 : if (format == ListFormat.multi || format == ListFormat.multiCompatible) { 60 6 : for (var i = 0; i < sub.length; i++) { 61 : final isCollection = 62 12 : sub[i] is Map || sub[i] is List || sub[i] is ListParam; 63 2 : if (listFormat == ListFormat.multi) { 64 : urlEncode( 65 2 : sub[i], 66 2 : '$path${isCollection ? leftBracket + '$i' + rightBracket : ''}', 67 : ); 68 : } else { 69 : // Forward compatibility 70 : urlEncode( 71 1 : sub[i], 72 1 : '$path$leftBracket${isCollection ? i : ''}$rightBracket', 73 : ); 74 : } 75 : } 76 : } else { 77 1 : urlEncode(sub.join(separatorChar), path); 78 : } 79 10 : } else if (sub is Map) { 80 13 : sub.forEach((k, v) { 81 3 : if (path == '') { 82 3 : urlEncode(v, '${encodeComponent(k as String)}'); 83 : } else { 84 : urlEncode( 85 : v, 86 1 : '$path$leftBracket${encodeComponent(k as String)}$rightBracket', 87 : ); 88 : } 89 : }); 90 : } else { 91 : var str = handler(path, sub); 92 4 : var isNotEmpty = str != null && str.trim().isNotEmpty; 93 : if (!first && isNotEmpty) { 94 2 : urlData.write('&'); 95 : } 96 : first = false; 97 : if (isNotEmpty) { 98 2 : urlData.write(str); 99 : } 100 : } 101 : } 102 : 103 : urlEncode(data, ''); 104 10 : return urlData.toString(); 105 : } 106 : 107 10 : String _getSeparatorChar(ListFormat collectionFormat) { 108 : switch (collectionFormat) { 109 10 : case ListFormat.csv: 110 : return ','; 111 10 : case ListFormat.ssv: 112 : return ' '; 113 10 : case ListFormat.tsv: 114 : return r'\t'; 115 10 : case ListFormat.pipes: 116 : return '|'; 117 : default: 118 : return ''; 119 : } 120 : } 121 : 122 10 : Map<String, V> caseInsensitiveKeyMap<V>([Map<String, V>? value]) { 123 10 : final map = LinkedHashMap<String, V>( 124 32 : equals: (key1, key2) => key1.toLowerCase() == key2.toLowerCase(), 125 30 : hashCode: (key) => key.toLowerCase().hashCode, 126 : ); 127 18 : if (value != null && value.isNotEmpty) map.addAll(value); 128 : return map; 129 : }