-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstringify.ts
More file actions
128 lines (109 loc) · 3.24 KB
/
stringify.ts
File metadata and controls
128 lines (109 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
// This module is browser compatible.
import { isNonNegativeInteger, isNumber } from "./deps.ts";
import type {
ContentRange,
InclRange,
RangeResp,
UnsatisfiedRange,
} from "./types.ts";
import {
assertValidRangeResp,
isRangeResp,
isRangeUnitFormat,
} from "./validate.ts";
import { Char, ContentRangeProp } from "./constants.ts";
export type ContentLength = UnsatisfiedRange;
/** Serialize {@link ContentRange} into string.
*
* @example
* ```ts
* import { stringifyContentRange } from "https://deno.land/x/content_range_parser@$VERSION/stringify.ts";
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* assertEquals(
* stringifyContentRange({
* rangeUnit: "bytes",
* firstPos: 0,
* lastPos: 100,
* completeLength: 1000,
* }),
* "bytes 0-100/1000",
* );
* assertEquals(
* stringifyContentRange({
* rangeUnit: "bytes",
* firstPos: 100,
* lastPos: 200,
* completeLength: undefined,
* }),
* "bytes 100-200/*",
* );
* assertEquals(
* stringifyContentRange({ rangeUnit: "bytes", completeLength: 1000 }),
* "bytes *\/1000",
* );
* ```
*
* @throws {TypeError} If the {@link ContentRange} is invalid.
*/
export function stringifyContentRange(contentRange: ContentRange): string {
const { rangeUnit } = contentRange;
if (!isRangeUnitFormat(rangeUnit)) {
throw TypeError(
`${ContentRangeProp.RangeUnit} is invalid <range-unit> syntax. "${rangeUnit}"`,
);
}
const _isRangeResp = isRangeResp(contentRange);
if (_isRangeResp) {
assertValidRangeResp(contentRange);
}
const right = _isRangeResp
? stringifyRangeResp(contentRange)
: stringifyUnsatisfiedRange(contentRange);
const field = `${contentRange.rangeUnit} ${right}`;
return field;
}
/**
* @throws {TypeError}
*/
export function stringifyRangeResp(rangeResp: RangeResp): string {
const { completeLength, ...inclRange } = rangeResp;
const inclRangeStr = stringifyInclRange(inclRange);
const right = isNumber(completeLength)
? stringifyCompleteLength({ completeLength })
: Char.Star;
return inclRangeStr + Char.Slash + right;
}
/**
* @throws {TypeError}
*/
export function stringifyInclRange(inclRange: InclRange): string {
const { firstPos, lastPos } = inclRange;
if (!isNonNegativeInteger(firstPos)) {
throw TypeError(message(ContentRangeProp.FirstPos, firstPos));
}
if (!isNonNegativeInteger(lastPos)) {
throw TypeError(message(ContentRangeProp.LastPos, lastPos));
}
return firstPos + Char.Hyphen + lastPos;
}
/**
* @throws {TypeError}
*/
export function stringifyCompleteLength(input: ContentLength): string {
const { completeLength } = input;
if (!isNonNegativeInteger(completeLength)) {
throw TypeError(message(ContentRangeProp.CompleteLength, completeLength));
}
return completeLength.toString();
}
export function stringifyUnsatisfiedRange(
unsatisfiedRange: UnsatisfiedRange,
): string {
const completeLengthStr = stringifyCompleteLength(unsatisfiedRange);
return Char.Star + Char.Slash + completeLengthStr;
}
function message(prop: ContentRangeProp, actual: number): string {
return `${prop} is not non-negative integer. ${actual}`;
}