LCOV - code coverage report
Current view: top level - src/formats - doi.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 27 27
Test Date: 2026-06-16 03:31:00 Functions: - 0 0
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright 2026 The Authors. See the AUTHORS file for details.
       2              : //
       3              : // Licensed under the Apache License, Version 2.0 (the "License");
       4              : // you may not use this file except in compliance with the License.
       5              : // You may obtain a copy of the License at
       6              : //
       7              : //      https://www.apache.org/licenses/LICENSE-2.0
       8              : //
       9              : // Unless required by applicable law or agreed to in writing, software
      10              : // distributed under the License is distributed on an "AS IS" BASIS,
      11              : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : // See the License for the specific language governing permissions and
      13              : // limitations under the License.
      14              : 
      15              : import 'dart:collection';
      16              : 
      17              : import 'package:betto_common/string.dart' show StringExtension;
      18              : 
      19              : import 'digit_string.dart' show DigitString;
      20              : 
      21              : /// Digital Object Identifier (DOI).
      22              : ///
      23              : /// See: [DOI HANDBOOK](https://www.doi.org/the-identifier/resources/handbook/)
      24              : class DOI {
      25              :   final DigitString directoryIndicator;
      26              :   final List<DigitString> _registrantCodes;
      27              :   final String suffix;
      28              : 
      29            1 :   DOI({
      30              :     required this.directoryIndicator,
      31              :     required List<DigitString> registrantCodes,
      32              :     required this.suffix,
      33            1 :   }) : _registrantCodes = [...registrantCodes];
      34              : 
      35            1 :   DOI._(this.directoryIndicator, this._registrantCodes, this.suffix);
      36              : 
      37            1 :   static DOI? tryParse(String value) {
      38              :     bool found;
      39              :     String prefix, suffix;
      40              : 
      41            1 :     (prefix, suffix, found) = value.cutFirst('/');
      42              :     if (!found) {
      43              :       return null;
      44              :     }
      45              : 
      46            2 :     if (prefix.isEmpty || suffix.isEmpty) {
      47              :       return null;
      48              :     }
      49              : 
      50              :     String directoryIndicatorStr, registrantCode;
      51            1 :     (directoryIndicatorStr, registrantCode, found) = prefix.cutFirst('.');
      52              : 
      53              :     if (!found) {
      54              :       return null;
      55              :     }
      56              : 
      57            2 :     if (directoryIndicatorStr.isEmpty || registrantCode.isEmpty) {
      58              :       return null;
      59              :     }
      60              : 
      61            1 :     final parseResult = DigitString.tryParse(directoryIndicatorStr);
      62              : 
      63              :     if (parseResult != null) {
      64              :       DigitString directoryIndicator = parseResult;
      65              : 
      66            1 :       final registrantCodes = _parseRegistrantCode(registrantCode);
      67            1 :       if (registrantCodes == null || registrantCodes.isEmpty) {
      68              :         return null;
      69              :       }
      70            1 :       final doi = DOI._(directoryIndicator, registrantCodes, suffix);
      71              :       return doi;
      72              :     }
      73              :     return null;
      74              :   }
      75              : 
      76            1 :   static List<DigitString>? _parseRegistrantCode(String registrantCode) {
      77            1 :     List<DigitString> registrantCodes = [];
      78              : 
      79            2 :     for (var digit in registrantCode.split('.')) {
      80            1 :       final digits = DigitString.tryParse(digit);
      81              :       if (digits == null) {
      82              :         return null;
      83              :       }
      84            1 :       registrantCodes.add(digits);
      85              :     }
      86              :     return registrantCodes;
      87              :   }
      88              : 
      89            1 :   UnmodifiableListView<DigitString> get registrantCodes =>
      90            2 :       UnmodifiableListView(_registrantCodes);
      91              : 
      92              :   // Uri get uri => Uri(scheme: 'https', host: 'doi.org', path: toString());
      93              : 
      94              :   /// DOI names are case insensitive.
      95              :   ///
      96              :   /// See: [DOI Handbook, Section 2.4](https://www.doi.org/the-identifier/resources/handbook/2_numbering#2.4)
      97            1 :   @override
      98              :   String toString() =>
      99            6 :       '$directoryIndicator.${_registrantCodes.join('.')}/$suffix'.toUpperCase();
     100              : 
     101              :   /// Uses https://doi.org
     102            3 :   Uri toUri() => Uri.https('doi.org', toString());
     103              : 
     104            1 :   @override
     105              :   bool operator ==(Object other) =>
     106            4 :       other is DOI && other.toString() == toString();
     107              : 
     108            1 :   @override
     109            3 :   int get hashCode => Object.hashAll([toString()]);
     110              : 
     111            2 :   static bool isValid(String value) => tryParse(value) != null;
     112              : }
        

Generated by: LCOV version 2.0-1