parseCsv static method

List<Map<String, dynamic>> parseCsv(
  1. List<String> lines
)

Parse CSV content lines (header + rows) into canonical links.

Implementation

static List<Map<String, dynamic>> parseCsv(List<String> lines) {
  final header = _splitCsvLine(lines[0]);
  // Map each header column index -> canonical key (or null if unrecognized).
  final colKey = header.map((h) {
    final n = _normHeader(h);
    for (final entry in _csvColumns.entries) {
      if (entry.value.contains(n)) return entry.key;
    }
    return null;
  }).toList();
  if (!colKey.any((k) => k != null)) {
    throw FdlParseError(
      'CSV header has no recognized columns. Expected at least a "link" column '
      '(the deep-link destination); got: ${header.join(', ')}',
    );
  }

  final rows = lines.sublist(1);
  if (rows.isEmpty) throw FdlParseError('CSV had a header but no data rows.');

  final out = <Map<String, dynamic>>[];
  for (var i = 0; i < rows.length; i++) {
    final cells = _splitCsvLine(rows[i]);
    String? val(String key) {
      final idx = colKey.indexOf(key);
      final v = idx == -1 ? null : (idx < cells.length ? cells[idx] : null);
      return (v == null || v == '') ? null : v;
    }

    final linkVal = val('link');
    final shortVal = val('shortlink');
    try {
      // Convenience: if there's no destination column but the short/url column
      // holds a full FDL long link (with its own ?link=... params), parse that.
      if (linkVal == null &&
          shortVal != null &&
          RegExp(r'[?&]link=').hasMatch(shortVal)) {
        out.add(parseUrl(shortVal));
        continue;
      }
      final info = <String, dynamic>{
        'link': linkVal,
        'shortLink': shortVal,
        'androidInfo': {
          'androidPackageName': val('apn'),
          'androidFallbackLink': val('afl'),
          'androidMinPackageVersionCode': val('amv'),
        },
        'iosInfo': {
          'iosBundleId': val('ibi'),
          'iosAppStoreId': val('isi'),
          'iosFallbackLink': val('ifl'),
          'iosCustomScheme': val('ius'),
          'iosMinimumVersion': val('imv'),
          'iosIpadBundleId': val('ipbi'),
          'iosIpadFallbackLink': val('ipfl'),
        },
        'navigationInfo': <String, dynamic>{},
        'analyticsInfo': {
          'googlePlayAnalytics': {
            'utmSource': val('utm_source'),
            'utmMedium': val('utm_medium'),
            'utmCampaign': val('utm_campaign'),
            'utmTerm': val('utm_term'),
            'utmContent': val('utm_content'),
            'gclid': val('gclid'),
          },
          'itunesConnectAnalytics': {
            'at': val('at'),
            'ct': val('ct'),
            'mt': val('mt'),
            'pt': val('pt'),
          },
        },
        'socialMetaTagInfo': {
          'socialTitle': val('st'),
          'socialDescription': val('sd'),
          'socialImageLink': val('si'),
        },
      };
      if (val('ofl') != null) info['otherFallbackLink'] = val('ofl');
      out.add(_normalizeObject(info));
    } on FdlParseError catch (e) {
      throw FdlParseError('Row #${i + 1}: ${e.message}');
    }
  }
  return out;
}