Skip to content

Commit 626031e

Browse files
authored
feat: add support for setConfigOption (#20)
Added API for updating the config option value. Signed-off-by: dankeboy36 <dankeboy36@gmail.com>
1 parent cbbde45 commit 626031e

File tree

4 files changed

+223
-0
lines changed

4 files changed

+223
-0
lines changed

README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ For a deeper understanding of how FQBN works, you should understand the
6161

6262
- [equals](#equals)
6363
- [sanitize](#sanitize)
64+
- [setConfigOption](#setconfigoption)
6465
- [toString](#tostring)
6566
- [withConfigOptions](#withconfigoptions)
6667
- [withFQBN](#withfqbn)
@@ -211,6 +212,83 @@ assert.ok(fqbn === fqbn.sanitize());
211212

212213
---
213214

215+
### setConfigOption
216+
217+
**setConfigOption**(`option`, `value`, `strict?`): [`FQBN`](#classesfqbnmd)
218+
219+
Sets the configuration option to a specified value and returns a new FQBN instance.
220+
221+
FQBNs are immutable, ensuring that existing configuration option keys are
222+
maintained in their original positions during the update. By default, it
223+
operates in non-strict mode, which allows for the insertion of new
224+
configuration values without error. If strict mode is enabled, any attempt
225+
to set a value for an absent configuration option will result in an error.
226+
227+
#### Parameters
228+
229+
| Name | Type | Default value | Description |
230+
| :-------- | :-------- | :------------ | :---------------------------------------------- |
231+
| `option` | `string` | `undefined` | the config option identifier to update. |
232+
| `value` | `string` | `undefined` | the selected configuration option value to set. |
233+
| `strict?` | `boolean` | `false` | Optional parameter to enable strict mode. |
234+
235+
#### Returns
236+
237+
[`FQBN`](#classesfqbnmd)
238+
239+
**`Example`**
240+
241+
```ts
242+
// Sets the configuration option to a specified value.
243+
const fqbn1 = new FQBN('arduino:samd:mkr1000:o1=v1');
244+
const fqbn2 = fqbn1.setConfigOption('o1', 'v2');
245+
assert.strictEqual(fqbn2.vendor, 'arduino');
246+
assert.strictEqual(fqbn2.arch, 'samd');
247+
assert.strictEqual(fqbn2.boardId, 'mkr1000');
248+
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
249+
```
250+
251+
**`Example`**
252+
253+
```ts
254+
// FQBNs are immutable.
255+
assert.deepStrictEqual(fqbn1.options, { o1: 'v1' });
256+
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
257+
```
258+
259+
**`Example`**
260+
261+
```ts
262+
// Always maintains the position of existing configuration option keys while updating the value.
263+
assert.strictEqual(
264+
new FQBN('arduino:samd:mkr1000:o1=v1,o2=v2')
265+
.setConfigOption('o1', 'v2')
266+
.toString(),
267+
'arduino:samd:mkr1000:o1=v2,o2=v2'
268+
);
269+
```
270+
271+
**`Example`**
272+
273+
```ts
274+
// Inserts new configuration values by default (non-strict mode).
275+
assert.strictEqual(
276+
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2').toString(),
277+
'arduino:samd:mkr1000:o1=v2'
278+
);
279+
```
280+
281+
**`Example`**
282+
283+
```ts
284+
// In strict mode, it throws an error when setting absent configuration option values.
285+
assert.throws(() =>
286+
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2', true)
287+
);
288+
```
289+
290+
---
291+
214292
### toString
215293

216294
**toString**(`skipOptions?`): `string`
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import assert from 'node:assert/strict';
2+
import { FQBN } from '../index';
3+
4+
// Sets the configuration option to a specified value.
5+
const fqbn1 = new FQBN('arduino:samd:mkr1000:o1=v1');
6+
const fqbn2 = fqbn1.setConfigOption('o1', 'v2');
7+
assert.strictEqual(fqbn2.vendor, 'arduino');
8+
assert.strictEqual(fqbn2.arch, 'samd');
9+
assert.strictEqual(fqbn2.boardId, 'mkr1000');
10+
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
11+
12+
// FQBNs are immutable.
13+
assert.deepStrictEqual(fqbn1.options, { o1: 'v1' });
14+
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
15+
16+
// Always maintains the position of existing configuration option keys while updating the value.
17+
assert.strictEqual(
18+
new FQBN('arduino:samd:mkr1000:o1=v1,o2=v2')
19+
.setConfigOption('o1', 'v2')
20+
.toString(),
21+
'arduino:samd:mkr1000:o1=v2,o2=v2'
22+
);
23+
24+
// Inserts new configuration values by default (non-strict mode).
25+
assert.strictEqual(
26+
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2').toString(),
27+
'arduino:samd:mkr1000:o1=v2'
28+
);
29+
30+
// In strict mode, it throws an error when setting absent configuration option values.
31+
assert.throws(() =>
32+
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2', true)
33+
);

src/__tests__/index.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,5 +395,45 @@ describe('fqbn', () => {
395395
);
396396
});
397397
});
398+
399+
describe('setConfigOption', () => {
400+
it('should be noop when setting the config value to the same', () => {
401+
const fqbn = new FQBN('a:b:c:o1=v1');
402+
const actual = fqbn.setConfigOption('o1', 'v1');
403+
assert.ok(fqbn === actual);
404+
});
405+
406+
it('should set the config value', () => {
407+
const fqbn = new FQBN('a:b:c:o1=v1');
408+
const actual = fqbn.setConfigOption('o1', 'v2');
409+
assert.strictEqual(actual.toString(), 'a:b:c:o1=v2');
410+
});
411+
412+
it('should not modify original FQBN', () => {
413+
const fqbn = new FQBN('a:b:c:o1=v1');
414+
fqbn.setConfigOption('o1', 'v2');
415+
assert.strictEqual(fqbn.toString(), 'a:b:c:o1=v1');
416+
});
417+
418+
it('should keep the config options order', () => {
419+
const fqbn = new FQBN('a:b:c:o1=v1,o2=v2');
420+
const actual = fqbn.setConfigOption('o1', 'v2');
421+
assert.strictEqual(actual.toString(), 'a:b:c:o1=v2,o2=v2');
422+
});
423+
424+
it('should insert when config value is absent and strict mode is false', () => {
425+
const fqbn = new FQBN('a:b:c');
426+
const actual = fqbn.setConfigOption('o1', 'v1');
427+
assert.strictEqual(actual.toString(), 'a:b:c:o1=v1');
428+
});
429+
430+
it('should error when config value is absent and strict mode is true', () => {
431+
const fqbn = new FQBN('a:b:c');
432+
assert.throws(
433+
() => fqbn.setConfigOption('o1', 'v1', true),
434+
/ConfigOptionError: .*/
435+
);
436+
});
437+
});
398438
});
399439
});

src/index.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,78 @@ export class FQBN {
246246
return new FQBN(serialize(vendor, arch, boardId, options));
247247
}
248248

249+
/**
250+
* Sets the configuration option to a specified value and returns a new FQBN instance.
251+
*
252+
* FQBNs are immutable, ensuring that existing configuration option keys are
253+
* maintained in their original positions during the update. By default, it
254+
* operates in non-strict mode, which allows for the insertion of new
255+
* configuration values without error. If strict mode is enabled, any attempt
256+
* to set a value for an absent configuration option will result in an error.
257+
*
258+
* @param option the config option identifier to update.
259+
* @param value the selected configuration option value to set.
260+
* @param [strict=false] Optional parameter to enable strict mode.
261+
*
262+
* @example
263+
* // Sets the configuration option to a specified value.
264+
* const fqbn1 = new FQBN('arduino:samd:mkr1000:o1=v1');
265+
* const fqbn2 = fqbn1.setConfigOption('o1', 'v2');
266+
* assert.strictEqual(fqbn2.vendor, 'arduino');
267+
* assert.strictEqual(fqbn2.arch, 'samd');
268+
* assert.strictEqual(fqbn2.boardId, 'mkr1000');
269+
* assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
270+
*
271+
* @example
272+
* // FQBNs are immutable.
273+
* assert.deepStrictEqual(fqbn1.options, { o1: 'v1' });
274+
* assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
275+
*
276+
* @example
277+
* // Always maintains the position of existing configuration option keys while updating the value.
278+
* assert.strictEqual(
279+
* new FQBN('arduino:samd:mkr1000:o1=v1,o2=v2')
280+
* .setConfigOption('o1', 'v2')
281+
* .toString(),
282+
* 'arduino:samd:mkr1000:o1=v2,o2=v2'
283+
* );
284+
*
285+
* @example
286+
* // Inserts new configuration values by default (non-strict mode).
287+
* assert.strictEqual(
288+
* new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2').toString(),
289+
* 'arduino:samd:mkr1000:o1=v2'
290+
* );
291+
*
292+
* @example
293+
* // In strict mode, it throws an error when setting absent configuration option values.
294+
* assert.throws(() =>
295+
* new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2', true)
296+
* );
297+
*/
298+
setConfigOption(
299+
option: ConfigOption['option'],
300+
value: ConfigValue['value'],
301+
strict = false
302+
): FQBN {
303+
const options = this.options ?? {};
304+
if (strict && !options[option]) {
305+
throw new ConfigOptionError(
306+
this.toString(),
307+
`Config option ${option} must be present in the FQBN (${this.toString()}) when using strict mode.`
308+
);
309+
}
310+
return this.withConfigOptions({
311+
option,
312+
values: [
313+
{
314+
value,
315+
selected: true,
316+
},
317+
],
318+
});
319+
}
320+
249321
/**
250322
* Creates an immutable copy of the current Fully Qualified Board Name (FQBN) after updating the custom board configuration options extracted from another FQBN.
251323
* New configuration options are added, and existing ones are updated accordingly.

0 commit comments

Comments
 (0)