aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main/src/stores/config/loadConfig.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/main/src/stores/config/loadConfig.ts')
-rw-r--r--packages/main/src/stores/config/loadConfig.ts146
1 files changed, 146 insertions, 0 deletions
diff --git a/packages/main/src/stores/config/loadConfig.ts b/packages/main/src/stores/config/loadConfig.ts
new file mode 100644
index 0000000..770d675
--- /dev/null
+++ b/packages/main/src/stores/config/loadConfig.ts
@@ -0,0 +1,146 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import {
22 applySnapshot,
23 IMSTArray,
24 IMSTMap,
25 IReferenceType,
26 IStateTreeNode,
27 IType,
28} from 'mobx-state-tree';
29import { nanoid } from 'nanoid';
30import slug from 'slug';
31
32import GlobalSettings from '../GlobalSettings';
33import type Profile from '../Profile';
34import type { ProfileSettingsSnapshotIn } from '../ProfileSettings';
35import type Service from '../Service';
36import type { ServiceSettingsSnapshotIn } from '../ServiceSettings';
37
38import type Config from './Config';
39import type ProfileConfig from './ProfileConfig';
40import type ServiceConfig from './ServiceConfig';
41
42function generateId(name?: string | undefined): string {
43 const nameSlug = typeof name === 'undefined' ? '' : slug(name);
44 return `${nameSlug}_${nanoid()}`;
45}
46
47function addMissingProfileIds(
48 profileConfigs: ProfileConfig[] | undefined,
49): [string, ProfileSettingsSnapshotIn][] {
50 return (profileConfigs ?? []).map((profileConfig) => {
51 const { id, ...settings } = profileConfig;
52 return [id === undefined ? generateId(settings.name) : id, settings];
53 });
54}
55
56function addMissingServiceIdsAndProfiles(
57 serviceConfigs: ServiceConfig[] | undefined,
58 profiles: [string, ProfileSettingsSnapshotIn][],
59): [string, ServiceSettingsSnapshotIn][] {
60 return (serviceConfigs ?? []).map((serviceConfig) => {
61 const { id, ...settings } = serviceConfig;
62 const { name } = settings;
63 let { profile: profileId } = settings;
64 if (profileId === undefined) {
65 profileId = generateId(name);
66 profiles.push([profileId, { name }]);
67 }
68 return [
69 id === undefined ? generateId(name) : id,
70 { ...settings, profile: profileId },
71 ];
72 });
73}
74
75type TypeWithSettings<C> = IType<
76 { id: string; settings: C },
77 unknown,
78 { settings: IStateTreeNode<IType<C, unknown, unknown>> }
79>;
80
81function applySettings<C, D extends TypeWithSettings<C>>(
82 current: IMSTArray<IReferenceType<D>>,
83 currentById: IMSTMap<D>,
84 toApply: [string, C][],
85): void {
86 const toApplyById = new Map(toApply);
87 const toDelete = new Set(currentById.keys());
88 toApplyById.forEach((settingsSnapshot, id) => {
89 const model = currentById.get(id);
90 if (model === undefined) {
91 currentById.set(id, {
92 id,
93 settings: settingsSnapshot,
94 });
95 } else {
96 toDelete.delete(id);
97 applySnapshot(model.settings, settingsSnapshot);
98 }
99 });
100 toDelete.forEach((id) => currentById.delete(id));
101 current.clear();
102 current.push(...toApply.map(([id]) => id));
103}
104
105export default function loadConfig(
106 target: {
107 readonly profiles: IMSTArray<IReferenceType<typeof Profile>>;
108 readonly profilesById: IMSTMap<typeof Profile>;
109 selectedService: Service | undefined;
110 readonly services: IMSTArray<IReferenceType<typeof Service>>;
111 readonly servicesById: IMSTMap<typeof Service>;
112 readonly settings: GlobalSettings;
113 },
114 config: Config,
115): void {
116 const {
117 profiles,
118 profilesById,
119 selectedService,
120 services,
121 servicesById,
122 settings,
123 } = target;
124 const { id: selectedServiceId } = selectedService ?? { id: undefined };
125 const {
126 profiles: profilesConfig,
127 services: servicesConfig,
128 ...settingsToApply
129 } = config;
130 const profilesToApply = addMissingProfileIds(profilesConfig);
131 const servicesToApply = addMissingServiceIdsAndProfiles(
132 servicesConfig,
133 profilesToApply,
134 );
135 applySettings(profiles, profilesById, profilesToApply);
136 applySettings(services, servicesById, servicesToApply);
137 applySnapshot(settings, settingsToApply);
138 let newSelectedService: Service | undefined;
139 if (selectedServiceId !== undefined) {
140 newSelectedService = servicesById.get(selectedServiceId);
141 }
142 if (newSelectedService === undefined && services.length > 0) {
143 [newSelectedService] = services;
144 }
145 target.selectedService = newSelectedService;
146}