@ygracs/chn-alias-list 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,759 @@
1
+ // [v0.1.033-20241021]
2
+
3
+ // === module init block ===
4
+
5
+ const {
6
+ isArray, isPlainObject,
7
+ valueToArray, valueToIndex, valueToIDString,
8
+ readAsString,
9
+ } = require('@ygracs/bsfoc-lib-js');
10
+
11
+ const {
12
+ loadJSONFromFileSync, saveJSONToFileSync,
13
+ } = require('#lib/file-helper.js');
14
+
15
+ // === module extra block (helper functions) ===
16
+
17
+ /**
18
+ * @function convertLangTextValueToString
19
+ * @param {array} data - contains a data to be printed
20
+ * @param {object} [opt]
21
+ * @returns {string}
22
+ * @inner
23
+ * @description Converts a <Lang, Text> key-pair to string
24
+ */
25
+ function convertLangTextValueToString(data, opt) {
26
+ let name = [ '', '' ];
27
+ let result = '';
28
+ if (isArray(data)) name = data;
29
+ let [ lang, text ] = name;
30
+ if (lang === '') {
31
+ result = text;
32
+ } else if (text !== '') {
33
+ result = `(${lang}): ${text}`;
34
+ };
35
+ return result;
36
+ };
37
+
38
+ // === module main block ===
39
+
40
+ /***
41
+ * (* constant definitions *)
42
+ */
43
+
44
+ /***
45
+ * (* function definitions *)
46
+ */
47
+
48
+ /**
49
+ * @typedef {Object} fsoDescr
50
+ * @property {boolean} isERR - flag
51
+ * @property {number|undefined} errCode - error code
52
+ * @property {string} errEvent - event ID
53
+ * @property {string} errMsg - event message
54
+ * @property {string} source - path to file
55
+ * @property {any} content - file content
56
+ * @description A fs ops description.
57
+ */
58
+
59
+ /**
60
+ * @typedef {Object} RVAL_loadaliasff
61
+ * @property {fsoDescr} descr - ops description
62
+ * @property {(null|TChnAliasItem|TChnAliasList)} result - loaded content
63
+ * @description A result of `loadAliasFromFile...`
64
+ */
65
+
66
+ /**
67
+ * @function loadAliasFromFileSync
68
+ * @param {string} src - a path to some file
69
+ * @param {any} [opt] - <reserved>
70
+ * @returns {RVAL_loadaliasff}
71
+ * @description Loads an alias from a file
72
+ */
73
+ function loadAliasFromFileSync(src, opt) {
74
+ let { descr, obj } = loadJSONFromFileSync(src, opt);
75
+ let result = null;
76
+ if (!descr.isERR) {
77
+ if (isArray(obj)) {
78
+ result = new TChnAliasList();
79
+ let count = result.loadItems(obj);
80
+ // // TODO: [?]
81
+ } else {
82
+ result = TChnAliasItem.create(obj);
83
+ };
84
+ };
85
+ return { descr, result };
86
+ };
87
+
88
+ /**
89
+ * @function saveAliasToFileSync
90
+ * @param {string} src - a path to some file
91
+ * @param {Array} content - content
92
+ * @param {any} [opt] - <reserved>
93
+ * @returns {fsoDescr}
94
+ * @description Saves an alias to a file
95
+ */
96
+ function saveAliasToFileSync(src, content, opt) {
97
+ const obj = isArray(content) ? content : null;
98
+ let result = null;
99
+ //===
100
+ result = saveJSONToFileSync(src, obj, opt);
101
+ return result;
102
+ };
103
+
104
+ /***
105
+ * (* class definitions *)
106
+ */
107
+
108
+ /**
109
+ * @classdesc This class implements an interface of the name record
110
+ */
111
+ class TChnNameRecord {
112
+ /** @property {string} */
113
+ #_lang;
114
+ /** @property {string} */
115
+ #_value;
116
+
117
+ /**
118
+ * @description Creates an instance of the name record
119
+ */
120
+ constructor() {
121
+ this.reset();
122
+ }
123
+
124
+ /**
125
+ * @property {string} - defines a language
126
+ * @readonly
127
+ */
128
+ get lang() {
129
+ return this.#_lang;
130
+ }
131
+
132
+ /**
133
+ * @property {string} - defines a value of the record
134
+ * @readonly
135
+ */
136
+ get text() {
137
+ return this.#_value;
138
+ }
139
+
140
+ /**
141
+ * @property {array} - a value of the record
142
+ */
143
+ get value() {
144
+ return [ this.#_lang, this.#_value ];
145
+ }
146
+
147
+ set value(data) {
148
+ this.setValue(data);
149
+ }
150
+
151
+ /**
152
+ * @param {any} data - a value of the record
153
+ * @returns {boolean}
154
+ * @description Sets the record value
155
+ */
156
+ setValue(data) {
157
+ let _data = data;
158
+ let isSucceed = false;
159
+ if (_data !== undefined) {
160
+ if (isPlainObject(_data)) {
161
+ let { lang, text } = _data;
162
+ _data = [ lang, text ];
163
+ } else {
164
+ _data = valueToArray(_data);
165
+ };
166
+ switch (_data.length) {
167
+ case 0: {
168
+ break;
169
+ }
170
+ case 1: {
171
+ _data = [ '', _data[0] ];
172
+ }
173
+ default: {
174
+ let [ lang, value = null ] = _data;
175
+ if (value !== null && typeof value !== 'boolean') {
176
+ value = readAsString(value, { numberToString: true });
177
+ lang = value !== '' ? valueToIDString(lang) : null;
178
+ if (lang === null) lang = '';
179
+ this.#_lang = lang;
180
+ this.#_value = value;
181
+ isSucceed = true;
182
+ };
183
+ }
184
+ };
185
+ };
186
+ return isSucceed;
187
+ }
188
+
189
+ /**
190
+ * @param {any} opt - <reserved>
191
+ * @returns {string}
192
+ * @description Returns value as a formated string
193
+ */
194
+ toFormatString(opt) {
195
+ return convertLangTextValueToString(this.value, opt);
196
+ }
197
+
198
+ /**
199
+ * @returns {void}
200
+ * @description Clears the record
201
+ */
202
+ reset() {
203
+ this.#_lang = '';
204
+ this.#_value = '';
205
+ }
206
+
207
+ };
208
+
209
+ /**
210
+ * @callback forEachProcEx
211
+ * @param {any} item
212
+ * @param {number} [index]
213
+ * @param {any[]} [arr]
214
+ * @returns {void}
215
+ * @description user defined procedure to process an array elements
216
+ */
217
+
218
+ /**
219
+ * @classdesc This class implements an interface of the name records list
220
+ */
221
+ class TChnNamesList {
222
+ /** @property {TChnNameRecord[]} */
223
+ #_items;
224
+
225
+ /**
226
+ * @description Creates an instance of the name records list
227
+ */
228
+ constructor() {
229
+ this.#_items = [];
230
+ }
231
+
232
+ /**
233
+ * @property {number} - contains a quantity of a name records
234
+ * @readonly
235
+ */
236
+ get count() {
237
+ return this.#_items.length;
238
+ }
239
+
240
+ /**
241
+ * @property {TChnNameRecord[]} - contains a list of a name records
242
+ * @readonly
243
+ */
244
+ get value() {
245
+ const result = [];
246
+ this.#_items.forEach((item, i) => {
247
+ result.push(item.value);
248
+ });
249
+ return result;
250
+ }
251
+
252
+ /**
253
+ * @returns {boolean}
254
+ * @description Returns a flag whether a list is empty or not
255
+ */
256
+ isEmpty() {
257
+ return this.count === 0;
258
+ }
259
+
260
+ /**
261
+ * @returns {boolean}
262
+ * @description Returns a flag whether a list has any members
263
+ */
264
+ isNotEmpty() {
265
+ return this.count > 0;
266
+ }
267
+
268
+ /**
269
+ * @param {any} value - a value to be verified
270
+ * @returns {boolean}
271
+ * @description Checks whether a given value is an index and fits
272
+ * an Index range within the instance
273
+ */
274
+ chkIndex(value) {
275
+ const index = valueToIndex(value);
276
+ return index !== -1 && index < this.count;
277
+ }
278
+
279
+ /**
280
+ * @param {string} value - a name
281
+ * @returns {number}
282
+ * @description Searches an index of a given name
283
+ */
284
+ getIndex(value) {
285
+ const opt = { numberToString: true };
286
+ const name = readAsString(value, opt);
287
+ let index = -1;
288
+ if (name !== '') {
289
+ index = this.#_items.findIndex((item) => name === item.text);
290
+ };
291
+ return index;
292
+ }
293
+
294
+ /**
295
+ * @param {number} value - an element index
296
+ * @returns {?TChnNameRecord}
297
+ * @description Returns a name record
298
+ */
299
+ getName(value) {
300
+ return this.chkIndex(value) ? this.#_items[Number(value)] : null;
301
+ }
302
+
303
+ /**
304
+ * @param {any} data - a value of a name record
305
+ * @returns {boolean}
306
+ * @description Adds a new name record to a list members
307
+ */
308
+ addName(data) {
309
+ const _data = data instanceof TChnNameRecord ? data.value : data;
310
+ const item = new TChnNameRecord();
311
+ const isSucceed = item.setValue(_data) && item.text !== '';
312
+ if (isSucceed) this.#_items.push(item);
313
+ return isSucceed;
314
+ }
315
+
316
+ /**
317
+ * @param {number} value - an element index
318
+ * @returns {boolean}
319
+ * @description Tries to delete a name record addressed by a given index
320
+ */
321
+ delName(value) {
322
+ let isSucceed = this.chkIndex(value)
323
+ if (isSucceed) this.#_items.splice(Number(value), 1);
324
+ return isSucceed;
325
+ }
326
+
327
+ /**
328
+ * @param {any} list - an element or a list of an elements
329
+ * @param {boolean} [opt=true] - defines whether to clear the list before load a new one
330
+ * @returns {number}
331
+ * @description Loads a new name records
332
+ */
333
+ loadNames(list, opt) {
334
+ const useClear = typeof opt === 'boolean' ? opt : true;
335
+ const items = valueToArray(list);
336
+ let count = 0;
337
+ if (items.length) {
338
+ if (useClear) this.clear();
339
+ items.forEach((item) => { if (this.addName(item)) count++; });
340
+ };
341
+ return count;
342
+ };
343
+
344
+ /**
345
+ * @returns {void}
346
+ * @description Removes all of the instance members
347
+ */
348
+ clear() {
349
+ this.#_items.length = 0;
350
+ }
351
+
352
+ /**
353
+ * @param {forEachProcEx} cb - a callback function
354
+ * @returns {void}
355
+ * @description Calls given function for each name record
356
+ */
357
+ forEach(cb) {
358
+ if (typeof cb === 'function') this.#_items.forEach(cb);
359
+ }
360
+
361
+ };
362
+
363
+ /**
364
+ * @classdesc This class implements an interface of the channel item
365
+ */
366
+ class TChnAliasItem {
367
+ /** @property {string} */
368
+ #_id = '';
369
+ /** @property {string} */
370
+ #_alias = '';
371
+ /** @property {TChnNamesList} */
372
+ #_names;
373
+ /** @property {object} */
374
+ #_status;
375
+
376
+ /**
377
+ * @description Creates an instance of the channel item
378
+ */
379
+ constructor() {
380
+ this.#_names = new TChnNamesList();
381
+ this.#_status = {
382
+ isEnabled: true,
383
+ isActive: true,
384
+ };
385
+ //===
386
+ }
387
+
388
+ /**
389
+ * @property {string} - defines a channel ID
390
+ */
391
+ get id() {
392
+ return this.#_id;
393
+ }
394
+
395
+ set id(value) {
396
+ this.setID(value);
397
+ }
398
+
399
+ /**
400
+ * @property {string} - defines a channel alias
401
+ */
402
+ get alias() {
403
+ return this.#_alias;
404
+ }
405
+
406
+ set alias(value) {
407
+ this.setAlias(value);
408
+ }
409
+
410
+ /**
411
+ * @property {TChnNamesList} - contains a list of a name records
412
+ * @readonly
413
+ */
414
+ get names() {
415
+ return this.#_names;
416
+ }
417
+
418
+ /**
419
+ * @property {string} - defines an instance status
420
+ * @readonly
421
+ */
422
+ get status() {
423
+ const { isActive, isEnabled } = this.#_status;
424
+ if (!isEnabled || this.#_id === '') return 'disabled';
425
+ return isActive ? 'up' : 'down';
426
+ }
427
+
428
+ /**
429
+ * @param {string} value - a channel ID
430
+ * @returns {boolean}
431
+ * @description Sets a channel ID
432
+ */
433
+ setID(value) {
434
+ const id = valueToIDString(value);
435
+ const isSucceed = id !== null;
436
+ if (isSucceed) this.#_id = id;
437
+ return isSucceed;
438
+ }
439
+
440
+ /**
441
+ * @param {string} value - a channel alias
442
+ * @returns {boolean}
443
+ * @description Sets a channel alias
444
+ */
445
+ setAlias(value) {
446
+ const alias = valueToIDString(value);
447
+ const isSucceed = alias !== null;
448
+ if (isSucceed) this.#_alias = alias;
449
+ return isSucceed;
450
+ }
451
+
452
+ /**
453
+ * @param {number} value
454
+ * @returns {?TChnNameRecord}
455
+ * @deprecated
456
+ * @description Returns a name record
457
+ * @see TChnNamesList.getName
458
+ */
459
+ getName(value) {
460
+ return this.#_names.getName(value);
461
+ }
462
+
463
+ /**
464
+ * @param {any} data - a name record
465
+ * @returns {boolean}
466
+ * @description Adds a new name record
467
+ * @see TChnNamesList.addName
468
+ */
469
+ addName(data) {
470
+ return this.#_names.addName(data);
471
+ }
472
+
473
+ /**
474
+ * @param {array} list - a list of an elements
475
+ * @param {boolean} [opt=true] - defines whether to clear the list before load a new one
476
+ * @returns {number}
477
+ * @description Loads a list of a new name records
478
+ * @see TChnNamesList.loadNames
479
+ */
480
+ loadNames(...args) {
481
+ return this.#_names.loadNames(...args);
482
+ }
483
+
484
+ /**
485
+ * @returns {void}
486
+ * @description Sets item status to enabled
487
+ */
488
+ enable() {
489
+ this.#_status.isEnabled = true;
490
+ }
491
+
492
+ /**
493
+ * @returns {void}
494
+ * @description Sets item status to disabled
495
+ */
496
+ disable() {
497
+ this.#_status.isEnabled = false;
498
+ }
499
+
500
+ /**
501
+ * @returns {void}
502
+ * @description Sets item status to active/running
503
+ */
504
+ up() {
505
+ this.#_status.isActive = true;
506
+ }
507
+
508
+ /**
509
+ * @returns {void}
510
+ * @description Sets item status to inactive/stopped
511
+ */
512
+ down() {
513
+ this.#_status.isActive = false;
514
+ }
515
+
516
+ /**
517
+ * @param {string} value - a status value
518
+ * @returns {boolean}
519
+ * @description Sets item status
520
+ */
521
+ setStatus(value) {
522
+ const status = readAsString(value);
523
+ let result = true;
524
+ switch (status) {
525
+ case 'disabled': {
526
+ this.disable();
527
+ break;
528
+ }
529
+ case 'enabled': {
530
+ this.enable();
531
+ break;
532
+ }
533
+ case 'up': {
534
+ this.up();
535
+ break;
536
+ }
537
+ case 'down': {
538
+ this.down();
539
+ break;
540
+ }
541
+ default: {
542
+ result = false;
543
+ }
544
+ };
545
+ return result;
546
+ }
547
+
548
+ /**
549
+ * @returns {Object}
550
+ * @protected
551
+ * @description Provides an interface to an instance representation
552
+ * for JSON.stringify()
553
+ */
554
+ toJSON() {
555
+ const { id, alias, names, status } = this;
556
+ return {
557
+ id,
558
+ alias,
559
+ name: names.value,
560
+ status,
561
+ };
562
+ }
563
+
564
+ /**
565
+ * @param {object} obj - an init data
566
+ * @returns {?TChnAliasItem}
567
+ * @static
568
+ * @description Creates a new alias element
569
+ */
570
+ static create(obj) {
571
+ let result = null;
572
+ if (isPlainObject(obj)) {
573
+ const item = new TChnAliasItem();
574
+ if (item.setID(obj.id)) {
575
+ const { isDisabled } = obj; /* only for compatibility purpose */
576
+ let { alias, name, status } = obj;
577
+ if (obj instanceof TChnAliasItem) name = obj.names.value;
578
+ item.setAlias(alias);
579
+ item.loadNames(name);
580
+ if (typeof isDisabled === 'boolean') {
581
+ // // TODO: process "status"
582
+ if (isDisabled) item.disable();
583
+ } else {
584
+ item.setStatus(status);
585
+ };
586
+ result = item;
587
+ };
588
+ };
589
+ return result;
590
+ }
591
+
592
+ };
593
+
594
+ /**
595
+ * @classdesc This class implements an interface of the channel items list
596
+ */
597
+ class TChnAliasList {
598
+ /** @property {TChnAliasItem[]} */
599
+ #_items;
600
+
601
+ /**
602
+ * @description Creates an instance of the channel items list
603
+ */
604
+ constructor() {
605
+ this.#_items = [];
606
+ }
607
+
608
+ /**
609
+ * @property {number} - contains a quantity of the elements
610
+ * @readonly
611
+ */
612
+ get count() {
613
+ return this.#_items.length;
614
+ }
615
+
616
+ /**
617
+ * @property {boolean} - defines if the instance has any items
618
+ * @readonly
619
+ * @deprecated
620
+ */
621
+ get hasItems() {
622
+ return this.#_items.length > 0;
623
+ }
624
+
625
+ /**
626
+ * @returns {boolean}
627
+ * @description Returns a flag whether a list is empty or not
628
+ */
629
+ isEmpty() {
630
+ return this.count === 0;
631
+ }
632
+
633
+ /**
634
+ * @returns {boolean}
635
+ * @description Returns a flag whether a list has any members
636
+ */
637
+ isNotEmpty() {
638
+ return this.count > 0;
639
+ }
640
+
641
+ /**
642
+ * @param {any} value - a value to be verified
643
+ * @returns {boolean}
644
+ * @description Checks whether a given value is an index and fits
645
+ * an Index range within the instance
646
+ */
647
+ chkIndex(value) {
648
+ const index = valueToIndex(value);
649
+ return index !== -1 && index < this.count;
650
+ }
651
+
652
+ /**
653
+ * @param {string} value - an element ID
654
+ * @returns {number}
655
+ * @description Searches an index of an element by its ID
656
+ */
657
+ searchIndexByID(value) {
658
+ const id = valueToIDString(value);
659
+ let index = -1;
660
+ if (id !== null && this.hasItems) {
661
+ index = this.#_items.findIndex((item) => id === item.id);
662
+ };
663
+ return index;
664
+ }
665
+
666
+ /**
667
+ * @param {number} value - an element index
668
+ * @returns {?TChnAliasItem}
669
+ * @description Returns an alias element by its index
670
+ */
671
+ getItem(value) {
672
+ return this.chkIndex(value) ? this.#_items[Number(value)] : null;
673
+ }
674
+
675
+ /**
676
+ * @param {string} value - an element ID
677
+ * @returns {?TChnAliasItem}
678
+ * @description Returns an alias element by its ID
679
+ */
680
+ getItemByID(value) {
681
+ return this.getItem(this.searchIndexByID(value));
682
+ }
683
+
684
+ /**
685
+ * @param {object} obj - an element to be added
686
+ * @returns {number}
687
+ * @description Adds an alias element
688
+ */
689
+ addItem(obj) {
690
+ const item = TChnAliasItem.create(obj);
691
+ let index = -1;
692
+ if (item !== null) {
693
+ const id = valueToIDString(item.id);
694
+ index = this.searchIndexByID(id);
695
+ if (index === -1) {
696
+ index = this.count;
697
+ this.#_items.push(item);
698
+ } else {
699
+ this.#_items[index] = item;
700
+ };
701
+ };
702
+ return index;
703
+ }
704
+
705
+ /**
706
+ * @param {number} value - an element index
707
+ * @returns {boolean}
708
+ * @description Tries to delete an element addressed by a given index
709
+ */
710
+ delItem(value) {
711
+ let isSucceed = this.chkIndex(value)
712
+ if (isSucceed) this.#_items.splice(Number(value), 1);
713
+ return isSucceed;
714
+ }
715
+
716
+ /**
717
+ * @param {array} list - a list of an elements
718
+ * @param {boolean} [opt=true] - defines whether to clear the list before load a new one
719
+ * @returns {number}
720
+ * @description Loads a list of a new alias elements
721
+ */
722
+ loadItems(list, opt) {
723
+ const useClear = typeof opt === 'boolean' ? opt : true;
724
+ let count = 0;
725
+ if (isArray(list)) {
726
+ if (list.length > 0 && useClear) this.clear();
727
+ list.forEach((item) => { if (this.addItem(item) !== -1) count++; });
728
+ };
729
+ return count;
730
+ };
731
+
732
+ /**
733
+ * @returns {void}
734
+ * @description removes all of the instance members
735
+ */
736
+ clear() {
737
+ this.#_items.length = 0;
738
+ }
739
+
740
+ /**
741
+ * @param {forEachProcEx} cb - a callback function
742
+ * @returns {void}
743
+ * @description Calls given function for each alias element
744
+ */
745
+ forEach(cb) {
746
+ if (typeof cb === 'function') this.#_items.forEach(cb);
747
+ }
748
+
749
+ };
750
+
751
+ // === module exports block ===
752
+
753
+ module.exports.loadAliasFromFileSync = loadAliasFromFileSync;
754
+ module.exports.saveAliasToFileSync = saveAliasToFileSync;
755
+
756
+ module.exports.TChnNameRecord = TChnNameRecord;
757
+ module.exports.TChnNamesList = TChnNamesList;
758
+ module.exports.TChnAliasItem = TChnAliasItem;
759
+ module.exports.TChnAliasList = TChnAliasList;