Skip to content

Commit

Permalink
feat: add support for setConfigOption (#20)
Browse files Browse the repository at this point in the history
Added API for updating the config option value.

Signed-off-by: dankeboy36 <[email protected]>
  • Loading branch information
dankeboy36 authored Dec 16, 2024
1 parent cbbde45 commit 626031e
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 0 deletions.
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ For a deeper understanding of how FQBN works, you should understand the

- [equals](#equals)
- [sanitize](#sanitize)
- [setConfigOption](#setconfigoption)
- [toString](#tostring)
- [withConfigOptions](#withconfigoptions)
- [withFQBN](#withfqbn)
Expand Down Expand Up @@ -211,6 +212,83 @@ assert.ok(fqbn === fqbn.sanitize());

---

### setConfigOption

**setConfigOption**(`option`, `value`, `strict?`): [`FQBN`](#classesfqbnmd)

Sets the configuration option to a specified value and returns a new FQBN instance.

FQBNs are immutable, ensuring that existing configuration option keys are
maintained in their original positions during the update. By default, it
operates in non-strict mode, which allows for the insertion of new
configuration values without error. If strict mode is enabled, any attempt
to set a value for an absent configuration option will result in an error.

#### Parameters

| Name | Type | Default value | Description |
| :-------- | :-------- | :------------ | :---------------------------------------------- |
| `option` | `string` | `undefined` | the config option identifier to update. |
| `value` | `string` | `undefined` | the selected configuration option value to set. |
| `strict?` | `boolean` | `false` | Optional parameter to enable strict mode. |

#### Returns

[`FQBN`](#classesfqbnmd)

**`Example`**

```ts
// Sets the configuration option to a specified value.
const fqbn1 = new FQBN('arduino:samd:mkr1000:o1=v1');
const fqbn2 = fqbn1.setConfigOption('o1', 'v2');
assert.strictEqual(fqbn2.vendor, 'arduino');
assert.strictEqual(fqbn2.arch, 'samd');
assert.strictEqual(fqbn2.boardId, 'mkr1000');
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
```

**`Example`**

```ts
// FQBNs are immutable.
assert.deepStrictEqual(fqbn1.options, { o1: 'v1' });
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
```

**`Example`**

```ts
// Always maintains the position of existing configuration option keys while updating the value.
assert.strictEqual(
new FQBN('arduino:samd:mkr1000:o1=v1,o2=v2')
.setConfigOption('o1', 'v2')
.toString(),
'arduino:samd:mkr1000:o1=v2,o2=v2'
);
```

**`Example`**

```ts
// Inserts new configuration values by default (non-strict mode).
assert.strictEqual(
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2').toString(),
'arduino:samd:mkr1000:o1=v2'
);
```

**`Example`**

```ts
// In strict mode, it throws an error when setting absent configuration option values.
assert.throws(() =>
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2', true)
);
```

---

### toString

**toString**(`skipOptions?`): `string`
Expand Down
33 changes: 33 additions & 0 deletions src/__examples__/setConfigOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import assert from 'node:assert/strict';
import { FQBN } from '../index';

// Sets the configuration option to a specified value.
const fqbn1 = new FQBN('arduino:samd:mkr1000:o1=v1');
const fqbn2 = fqbn1.setConfigOption('o1', 'v2');
assert.strictEqual(fqbn2.vendor, 'arduino');
assert.strictEqual(fqbn2.arch, 'samd');
assert.strictEqual(fqbn2.boardId, 'mkr1000');
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });

// FQBNs are immutable.
assert.deepStrictEqual(fqbn1.options, { o1: 'v1' });
assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });

// Always maintains the position of existing configuration option keys while updating the value.
assert.strictEqual(
new FQBN('arduino:samd:mkr1000:o1=v1,o2=v2')
.setConfigOption('o1', 'v2')
.toString(),
'arduino:samd:mkr1000:o1=v2,o2=v2'
);

// Inserts new configuration values by default (non-strict mode).
assert.strictEqual(
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2').toString(),
'arduino:samd:mkr1000:o1=v2'
);

// In strict mode, it throws an error when setting absent configuration option values.
assert.throws(() =>
new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2', true)
);
40 changes: 40 additions & 0 deletions src/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,5 +395,45 @@ describe('fqbn', () => {
);
});
});

describe('setConfigOption', () => {
it('should be noop when setting the config value to the same', () => {
const fqbn = new FQBN('a:b:c:o1=v1');
const actual = fqbn.setConfigOption('o1', 'v1');
assert.ok(fqbn === actual);
});

it('should set the config value', () => {
const fqbn = new FQBN('a:b:c:o1=v1');
const actual = fqbn.setConfigOption('o1', 'v2');
assert.strictEqual(actual.toString(), 'a:b:c:o1=v2');
});

it('should not modify original FQBN', () => {
const fqbn = new FQBN('a:b:c:o1=v1');
fqbn.setConfigOption('o1', 'v2');
assert.strictEqual(fqbn.toString(), 'a:b:c:o1=v1');
});

it('should keep the config options order', () => {
const fqbn = new FQBN('a:b:c:o1=v1,o2=v2');
const actual = fqbn.setConfigOption('o1', 'v2');
assert.strictEqual(actual.toString(), 'a:b:c:o1=v2,o2=v2');
});

it('should insert when config value is absent and strict mode is false', () => {
const fqbn = new FQBN('a:b:c');
const actual = fqbn.setConfigOption('o1', 'v1');
assert.strictEqual(actual.toString(), 'a:b:c:o1=v1');
});

it('should error when config value is absent and strict mode is true', () => {
const fqbn = new FQBN('a:b:c');
assert.throws(
() => fqbn.setConfigOption('o1', 'v1', true),
/ConfigOptionError: .*/
);
});
});
});
});
72 changes: 72 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,78 @@ export class FQBN {
return new FQBN(serialize(vendor, arch, boardId, options));
}

/**
* Sets the configuration option to a specified value and returns a new FQBN instance.
*
* FQBNs are immutable, ensuring that existing configuration option keys are
* maintained in their original positions during the update. By default, it
* operates in non-strict mode, which allows for the insertion of new
* configuration values without error. If strict mode is enabled, any attempt
* to set a value for an absent configuration option will result in an error.
*
* @param option the config option identifier to update.
* @param value the selected configuration option value to set.
* @param [strict=false] Optional parameter to enable strict mode.
*
* @example
* // Sets the configuration option to a specified value.
* const fqbn1 = new FQBN('arduino:samd:mkr1000:o1=v1');
* const fqbn2 = fqbn1.setConfigOption('o1', 'v2');
* assert.strictEqual(fqbn2.vendor, 'arduino');
* assert.strictEqual(fqbn2.arch, 'samd');
* assert.strictEqual(fqbn2.boardId, 'mkr1000');
* assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
*
* @example
* // FQBNs are immutable.
* assert.deepStrictEqual(fqbn1.options, { o1: 'v1' });
* assert.deepStrictEqual(fqbn2.options, { o1: 'v2' });
*
* @example
* // Always maintains the position of existing configuration option keys while updating the value.
* assert.strictEqual(
* new FQBN('arduino:samd:mkr1000:o1=v1,o2=v2')
* .setConfigOption('o1', 'v2')
* .toString(),
* 'arduino:samd:mkr1000:o1=v2,o2=v2'
* );
*
* @example
* // Inserts new configuration values by default (non-strict mode).
* assert.strictEqual(
* new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2').toString(),
* 'arduino:samd:mkr1000:o1=v2'
* );
*
* @example
* // In strict mode, it throws an error when setting absent configuration option values.
* assert.throws(() =>
* new FQBN('arduino:samd:mkr1000').setConfigOption('o1', 'v2', true)
* );
*/
setConfigOption(
option: ConfigOption['option'],
value: ConfigValue['value'],
strict = false
): FQBN {
const options = this.options ?? {};
if (strict && !options[option]) {
throw new ConfigOptionError(
this.toString(),
`Config option ${option} must be present in the FQBN (${this.toString()}) when using strict mode.`
);
}
return this.withConfigOptions({
option,
values: [
{
value,
selected: true,
},
],
});
}

/**
* Creates an immutable copy of the current Fully Qualified Board Name (FQBN) after updating the custom board configuration options extracted from another FQBN.
* New configuration options are added, and existing ones are updated accordingly.
Expand Down

0 comments on commit 626031e

Please sign in to comment.