All files / media_types / _util.ts

100.00% Branches 85/85
100.00% Functions 11/11
100.00% Lines 123/123
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
 
 
 
 
 
 
 
 
 
x20
x92
x92
x29
x29
x92
x2
x2
x61
x92
 
x20
x73
x7
x7
x70
x27
x27
x39
x60
x357
x357
x36
x36
x321
x357
x4
x4
x4
x4
x357
x2
x2
x315
x315
x1
x73
 
x20
x20
 
x63
x63
x2
x2
x61
x61
x61
x61
x61
x63
x2
x2
x59
x59
x59
x63
x6
x6
x53
x53
x63
 
x20
x10
x10
x2
x2
x8
x8
x10
x1
x1
x10
x1
x1
x6
x10
x1
x1
x5
x10
 
x389
x389
x389
x2139
x2139
x73
x73
x2139
x316
x389
 
x20
x81
x2
x2
 
x79
x81
 
x20
x300
x3
x3
x297
x300
 
x2139
x2139
x2139
 
x2139
x2139
x2139
x2139
 
x20
x2167
x2167
 
x20
x20
 
x20
x73
x400
x400
x400
x400
x5
x5
x400
x68
x73















































































































































// Copyright 2018-2026 the Deno authors. MIT license.

export interface DBEntry {
  source: string;
  compressible?: boolean;
  charset?: string;
  extensions?: string[];
}

export function consumeToken(v: string): [token: string, rest: string] {
  const notPos = indexOf(v, isNotTokenChar);
  if (notPos === -1) {
    return [v, ""];
  }
  if (notPos === 0) {
    return ["", v];
  }
  return [v.slice(0, notPos), v.slice(notPos)];
}

export function consumeValue(v: string): [value: string, rest: string] {
  if (!v) {
    return ["", v];
  }
  if (v[0] !== `"`) {
    return consumeToken(v);
  }
  let value = "";
  for (let i = 1; i < v.length; i++) {
    const r = v[i];
    if (r === `"`) {
      return [value, v.slice(i + 1)];
    }
    const next = v[i + 1];
    if (r === "\\" && typeof next === "string" && isTSpecial(next)) {
      value += next;
      i++;
      continue;
    }
    if (r === "\r" || r === "\n") {
      return ["", v];
    }
    value += v[i];
  }
  return ["", v];
}

export function consumeMediaParam(
  v: string,
): [key: string, value: string, rest: string] {
  let rest = v.trimStart();
  if (!rest.startsWith(";")) {
    return ["", "", v];
  }
  rest = rest.slice(1);
  rest = rest.trimStart();
  let param: string;
  [param, rest] = consumeToken(rest);
  param = param.toLowerCase();
  if (!param) {
    return ["", "", v];
  }
  rest = rest.slice(1);
  rest = rest.trimStart();
  const [value, rest2] = consumeValue(rest);
  if (value === "" && rest2 === rest) {
    return ["", "", v];
  }
  rest = rest2;
  return [param, value, rest];
}

export function decode2331Encoding(v: string): string | undefined {
  const sv = v.split(`'`, 3);
  if (sv.length !== 3) {
    return undefined;
  }
  const [sv0, , sv2] = sv as [string, string, string];
  const charset = sv0.toLowerCase();
  if (!charset) {
    return undefined;
  }
  if (charset !== "us-ascii" && charset !== "utf-8") {
    return undefined;
  }
  const encv = decodeURI(sv2);
  if (!encv) {
    return undefined;
  }
  return encv;
}

function indexOf<T>(s: Iterable<T>, fn: (s: T) => boolean): number {
  let i = -1;
  for (const v of s) {
    i++;
    if (fn(v)) {
      return i;
    }
  }
  return -1;
}

export function isIterator<T>(obj: unknown): obj is Iterable<T> {
  if (obj === null || obj === undefined) {
    return false;
  }
  // deno-lint-ignore no-explicit-any
  return typeof (obj as any)[Symbol.iterator] === "function";
}

export function isToken(s: string): boolean {
  if (!s) {
    return false;
  }
  return indexOf(s, isNotTokenChar) < 0;
}

function isNotTokenChar(r: string): boolean {
  return !isTokenChar(r);
}

function isTokenChar(r: string): boolean {
  const code = r.charCodeAt(0);
  return code > 0x20 && code < 0x7f && !isTSpecial(r);
}

export function isTSpecial(r: string): boolean {
  return r[0] ? `()<>@,;:\\"/[]?=`.includes(r[0]) : false;
}

const CHAR_CODE_SPACE = " ".charCodeAt(0);
const CHAR_CODE_TILDE = "~".charCodeAt(0);

export function needsEncoding(s: string): boolean {
  for (const b of s) {
    const charCode = b.charCodeAt(0);
    if (
      (charCode < CHAR_CODE_SPACE || charCode > CHAR_CODE_TILDE) && b !== "\t"
    ) {
      return true;
    }
  }
  return false;
}