Parse a format specification.
| 69 | |
| 70 | /// Parse a format specification. |
| 71 | bool |
| 72 | Spec::parse(TextView fmt) { |
| 73 | TextView num; // temporary for number parsing. |
| 74 | |
| 75 | _name = fmt.take_prefix_at(':'); |
| 76 | // if it's parsable as a number, treat it as an index. |
| 77 | num = _name; |
| 78 | auto n = svto_radix<10>(num); |
| 79 | if (num.empty()) { |
| 80 | _idx = static_cast<decltype(_idx)>(n); |
| 81 | } |
| 82 | |
| 83 | if (fmt.size()) { |
| 84 | TextView sz = fmt.take_prefix_at(':'); // the format specifier. |
| 85 | _ext = fmt; // anything past the second ':' is the extension. |
| 86 | if (sz.size()) { |
| 87 | // fill and alignment |
| 88 | if ('%' == *sz) { // enable URI encoding of the fill character so |
| 89 | // metasyntactic chars can be used if needed. |
| 90 | if (sz.size() < 4) { |
| 91 | throw std::invalid_argument("Fill URI encoding without 2 hex characters and align mark"); |
| 92 | } |
| 93 | if (Align::NONE == (_align = align_of(sz[3]))) { |
| 94 | throw std::invalid_argument("Fill URI without alignment mark"); |
| 95 | } |
| 96 | char d1 = sz[1], d0 = sz[2]; |
| 97 | if (!isxdigit(d0) || !isxdigit(d1)) { |
| 98 | throw std::invalid_argument("URI encoding with non-hex characters"); |
| 99 | } |
| 100 | _fill = isdigit(d0) ? d0 - '0' : tolower(d0) - 'a' + 10; |
| 101 | _fill += (isdigit(d1) ? d1 - '0' : tolower(d1) - 'a' + 10) << 4; |
| 102 | sz += 4; |
| 103 | } else if (sz.size() > 1 && Align::NONE != (_align = align_of(sz[1]))) { |
| 104 | _fill = *sz; |
| 105 | sz += 2; |
| 106 | } else if (Align::NONE != (_align = align_of(*sz))) { |
| 107 | ++sz; |
| 108 | } |
| 109 | if (!sz.size()) { |
| 110 | return true; |
| 111 | } |
| 112 | // sign |
| 113 | if (is_sign(*sz)) { |
| 114 | _sign = *sz; |
| 115 | if (!(++sz).size()) { |
| 116 | return true; |
| 117 | } |
| 118 | } |
| 119 | // radix prefix |
| 120 | if ('#' == *sz) { |
| 121 | _radix_lead_p = true; |
| 122 | if (!(++sz).size()) { |
| 123 | return true; |
| 124 | } |
| 125 | } |
| 126 | // 0 fill for integers |
| 127 | if ('0' == *sz) { |
| 128 | if (Align::NONE == _align) { |
no test coverage detected