import 'package:logging/logging.dart'; import 'package:result_monad/result_monad.dart'; import 'package:uuid/uuid.dart'; import '../models/exec_error.dart'; import 'paging_data.dart'; final _logger = Logger('PagedResponse'); class PagedResponse { String id; PagingData? previous; PagingData? next; T data; PagedResponse(this.data, {String? id, this.previous, this.next}) : id = id ?? const Uuid().v4(); bool get hasMorePages => previous != null || next != null; static Result, ExecError> fromLinkHeader( String? linkHeader, T data) { if (linkHeader == null || linkHeader.isEmpty) { return Result.ok(PagedResponse(data)); } String? previousPage; String? nextPage; for (String linkTerms in linkHeader.trim().split(',')) { if (linkHeader.isEmpty) { return buildErrorResult( type: ErrorType.parsingError, message: 'Link header element is blank', ); } final paging = linkTerms.split(';'); if (paging.length != 2) { return buildErrorResult( type: ErrorType.parsingError, message: 'Incorrect number of elements, ${paging.length} != 2, for: $linkTerms', ); } final urlPieceString = paging.first.trim(); if (!urlPieceString.startsWith('<') && !urlPieceString.endsWith('>')) { return buildErrorResult( type: ErrorType.parsingError, message: 'Link URL is malformed (no leading trailing <>): $urlPieceString', ); } final url = urlPieceString.substring(1, urlPieceString.length - 1); final directionString = paging.last.trim(); if (directionString == 'rel="prev"') { previousPage = url; } else if (directionString == 'rel="next"') { nextPage = url; } else { _logger.info('Unknown paging data: $directionString for url: $url'); } } return Result.ok(PagedResponse( data, previous: previousPage == null ? null : PagingData.fromQueryParameters( Uri.parse(previousPage), ), next: nextPage == null ? null : PagingData.fromQueryParameters( Uri.parse(nextPage), ), )); } PagedResponse map(T2 Function(T data) func) => PagedResponse( func(data), previous: previous, next: next, id: id, ); @override String toString() { return 'PagedResponse{previous: $previous, next: $next, data: $data}'; } @override bool operator ==(Object other) => identical(this, other) || other is PagedResponse && runtimeType == other.runtimeType && previous == other.previous && next == other.next && data == other.data; @override int get hashCode => previous.hashCode ^ next.hashCode ^ data.hashCode; }