typespec-rust-emitter 0.10.5 → 0.10.7

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.
@@ -1,234 +0,0 @@
1
- import "typespec-rust-emitter";
2
- import "@typespec/events";
3
-
4
- using TypeSpec.Events;
5
-
6
- namespace Mtlkms.Learning.Models {
7
- // ── COMMON ─────────────────────────────────────────────────────────────────
8
-
9
- @error
10
- model ApiError {
11
- @doc("Machine-readable error code.")
12
- code: string;
13
-
14
- @doc("Human-readable message.")
15
- message: string;
16
- }
17
-
18
- model NotFoundError extends ApiError {
19
- code: "NOT_FOUND";
20
- }
21
- model ValidationError extends ApiError {
22
- code: "VALIDATION_ERROR";
23
- }
24
- model ConflictError extends ApiError {
25
- code: "CONFLICT";
26
- }
27
-
28
- // ── SCALARS & ENUMS ───────────────────────────────────────────────────────
29
-
30
- @format("uuid")
31
- scalar Uuid extends string;
32
-
33
- @pattern("^#[0-9A-Fa-f]{6}$")
34
- scalar HexColor extends string;
35
-
36
- @rustDerive("sqlx::Type")
37
- @rustAttr("sqlx(type_name = \"study_status\")")
38
- enum StudyStatus {
39
- Starting: "Starting",
40
- Paused: "Paused",
41
- Stopped: "Stopped",
42
- }
43
-
44
- enum ChartGranularity {
45
- Day: "day",
46
- Week: "week",
47
- Month: "month",
48
- Year: "year",
49
- }
50
-
51
- // ── AGGREGATION MIXIN ──────────────────────────────────────────────────────
52
-
53
- @doc("Duration statistics calculated in real-time from timelogs. Unit: seconds.")
54
- model DurationStats {
55
- durationTotal: int64;
56
- durationDay: int64;
57
- durationWeek: int64;
58
- durationMonth: int64;
59
- }
60
-
61
- model CalendarHeatmapItem {
62
- date: string;
63
- duration: int64;
64
- }
65
-
66
- // ── MODELS: GROUP ─────────────────────────────────────────────────────────
67
-
68
- model Group {
69
- id: int64;
70
- accountId: Uuid;
71
- createdAt: utcDateTime;
72
- deletedAt: utcDateTime | null;
73
-
74
- @maxLength(255)
75
- name: string;
76
-
77
- @maxLength(44)
78
- icon: string;
79
-
80
- colorText: HexColor;
81
- colorBg: HexColor;
82
- }
83
-
84
- @rustDerive("sqlx::FromRow")
85
- model GroupStatistics {
86
- ...Group;
87
- subjectCount: int64;
88
- status: StudyStatus;
89
- ...DurationStats;
90
- }
91
-
92
- model CreateGroupBody {
93
- @maxLength(255)
94
- name: string;
95
-
96
- @maxLength(44)
97
- icon: string;
98
-
99
- colorText: HexColor;
100
- colorBg: HexColor;
101
- }
102
-
103
- model UpdateGroupBody {
104
- @maxLength(255)
105
- name?: string;
106
-
107
- @maxLength(44)
108
- icon?: string;
109
-
110
- colorText?: HexColor;
111
- colorBg?: HexColor;
112
- }
113
-
114
- // ── MODELS: SUBJECT ───────────────────────────────────────────────────────
115
-
116
- model Subject {
117
- id: int64;
118
- accountId: Uuid;
119
- groupId: int64;
120
- createdAt: utcDateTime;
121
- deletedAt: utcDateTime | null;
122
-
123
- @maxLength(255)
124
- name: string;
125
-
126
- @maxLength(44)
127
- icon: string;
128
-
129
- colorText: HexColor;
130
- colorBg: HexColor;
131
- }
132
-
133
- model SubjectStatistics {
134
- ...Subject;
135
- status: StudyStatus;
136
- ...DurationStats;
137
- }
138
-
139
- model CreateSubjectBody {
140
- @maxLength(255)
141
- name: string;
142
-
143
- @maxLength(44)
144
- icon: string;
145
-
146
- colorText: HexColor;
147
- colorBg: HexColor;
148
- }
149
-
150
- model UpdateSubjectBody {
151
- @maxLength(255)
152
- name?: string;
153
-
154
- @maxLength(44)
155
- icon?: string;
156
-
157
- colorText?: HexColor;
158
- colorBg?: HexColor;
159
- }
160
-
161
- // ── MODELS: SESSION ───────────────────────────────────────────────────────
162
-
163
- model Log {
164
- id: int64;
165
- accountId: Uuid;
166
- subjectId: int64;
167
- startedAt: utcDateTime;
168
- stoppedAt: utcDateTime | null;
169
- logContent: string | null;
170
- status: StudyStatus;
171
- }
172
-
173
- model Timelog {
174
- id: int64;
175
- accountId: Uuid;
176
- logId: int64;
177
- subjectId: int64;
178
- startedAt: utcDateTime;
179
- stoppedAt: utcDateTime | null;
180
- durationInSeconds: int64;
181
- }
182
-
183
- model LogWithTimelogs {
184
- ...Log;
185
- timelogs: Timelog[];
186
- }
187
-
188
- @doc("Event payload for learning data changes.")
189
- model SessionEvent {
190
- eventType: "session" | "session_pause" | "session_resume" | "session_stop";
191
- session: LogWithTimelogs;
192
- }
193
-
194
- @doc("Event payload for group CRUD.")
195
- model GroupEvent {
196
- eventType: "group_created" | "group_updated" | "group_deleted";
197
- group: Group;
198
- }
199
-
200
- @doc("Event payload for subject CRUD.")
201
- model SubjectEvent {
202
- eventType:
203
- | "subject_created"
204
- | "subject_updated"
205
- | "subject_deleted"
206
- | "subject_moved";
207
- subject: Subject;
208
- }
209
-
210
- @doc("Union of all learning events for SSE.")
211
- @events
212
- union LearningEvents {
213
- sessionEvent: SessionEvent,
214
- groupEvent: GroupEvent,
215
- subjectEvent: SubjectEvent,
216
- }
217
-
218
- @doc("Response after start/resume — returns the log and the newly opened timelog segment.")
219
- model SessionOpenedResponse {
220
- log: Log;
221
- timelog: Timelog;
222
- }
223
-
224
- @doc("Response after pause/stop — returns the log and the newly closed timelog segment.")
225
- model SessionClosedResponse {
226
- log: Log;
227
- timelog: Timelog;
228
- }
229
-
230
- @doc("Optional payload for start or stop operations — contains a note for the session.")
231
- model SessionNoteBody {
232
- logContent?: string;
233
- }
234
- }
@@ -1,364 +0,0 @@
1
- import "@typespec/http";
2
- import "@typespec/sse";
3
- import "./models.tsp";
4
-
5
- using TypeSpec.Http;
6
- using TypeSpec.SSE;
7
- using Mtlkms.Learning.Models;
8
-
9
- @route("/accounts/{accountId}/learning")
10
- namespace Mtlkms.Learning;
11
-
12
- @route("/groups")
13
- @tag("Groups")
14
- namespace Groups {
15
- @get
16
- @doc("Retrieve a list of all groups for the current account (from view_learning_group).")
17
- op list(@path accountId: Uuid): {
18
- @statusCode statusCode: 200;
19
- @body body: GroupStatistics[];
20
- } | {
21
- @statusCode statusCode: 401;
22
- @body error: ApiError;
23
- };
24
-
25
- @get
26
- @route("/calendar")
27
- @doc("Get calendar heatmap data for a specific group by month.")
28
- op calendar(
29
- @path accountId: Uuid,
30
- @path groupId: Uuid,
31
- @query userTimezone: string,
32
- @query year: int32,
33
- @query month: int32,
34
- ): {
35
- @statusCode statusCode: 200;
36
- @body body: CalendarHeatmapItem[];
37
- } | {
38
- @statusCode statusCode: 401;
39
- @body error: ApiError;
40
- };
41
-
42
- @post
43
- @useAuth(BearerAuth)
44
- @doc("Create a new group.")
45
- op create(@path accountId: Uuid, @body body: CreateGroupBody): {
46
- @statusCode statusCode: 201;
47
- @body body: Group;
48
- } | {
49
- @statusCode statusCode: 400;
50
- @body error: ValidationError;
51
- } | {
52
- @statusCode statusCode: 401;
53
- @body error: ApiError;
54
- };
55
-
56
- @get
57
- @route("/{id}")
58
- @doc("Get the details of a group by its ID.")
59
- op getById(@path accountId: Uuid, @path id: int64): {
60
- @statusCode statusCode: 200;
61
- @body body: GroupStatistics;
62
- } | {
63
- @statusCode statusCode: 401;
64
- @body error: ApiError;
65
- } | {
66
- @statusCode statusCode: 404;
67
- @body error: NotFoundError;
68
- };
69
-
70
- @patch
71
- @route("/{id}")
72
- @useAuth(BearerAuth)
73
- @doc("Update a group. Only provide the fields that need to be changed.")
74
- op update(
75
- @path accountId: Uuid,
76
- @path id: int64,
77
- @body body: UpdateGroupBody,
78
- ):
79
- | {
80
- @statusCode statusCode: 200;
81
- @body body: Group;
82
- }
83
- | {
84
- @statusCode statusCode: 400;
85
- @body error: ValidationError;
86
- }
87
- | {
88
- @statusCode statusCode: 401;
89
- @body error: ApiError;
90
- }
91
- | {
92
- @statusCode statusCode: 404;
93
- @body error: NotFoundError;
94
- };
95
-
96
- @delete
97
- @route("/{id}")
98
- @useAuth(BearerAuth)
99
- @doc("Soft delete a group. All subjects within the group will also be soft deleted.")
100
- op delete(@path accountId: Uuid, @path id: int64): {
101
- @statusCode statusCode: 204;
102
- } | {
103
- @statusCode statusCode: 401;
104
- @body error: ApiError;
105
- } | {
106
- @statusCode statusCode: 404;
107
- @body error: NotFoundError;
108
- };
109
- }
110
-
111
- @route("/groups/{groupId}/subjects")
112
- @tag("Subjects")
113
- namespace Subjects {
114
- @get
115
- @doc("Retrieve a list of subjects for a group (from view_subject_statistics).")
116
- op list(@path accountId: Uuid, @path groupId: int64): {
117
- @statusCode statusCode: 200;
118
- @body body: SubjectStatistics[];
119
- } | {
120
- @statusCode statusCode: 401;
121
- @body error: ApiError;
122
- } | {
123
- @statusCode statusCode: 404;
124
-
125
- @doc("The group does not exist.")
126
- @body
127
- error: NotFoundError;
128
- };
129
-
130
- @post
131
- @useAuth(BearerAuth)
132
- @doc("Create a new subject within the specified group.")
133
- op create(
134
- @path accountId: Uuid,
135
- @path groupId: int64,
136
- @body body: CreateSubjectBody,
137
- ):
138
- | {
139
- @statusCode statusCode: 201;
140
- @body body: Subject;
141
- }
142
- | {
143
- @statusCode statusCode: 400;
144
- @body error: ValidationError;
145
- }
146
- | {
147
- @statusCode statusCode: 401;
148
- @body error: ApiError;
149
- }
150
- | {
151
- @statusCode statusCode: 404;
152
- @body error: NotFoundError;
153
- };
154
-
155
- @get
156
- @route("/{id}")
157
- @doc("Get the details of a subject by its ID.")
158
- op getById(@path accountId: Uuid, @path groupId: int64, @path id: int64): {
159
- @statusCode statusCode: 200;
160
- @body body: SubjectStatistics;
161
- } | {
162
- @statusCode statusCode: 401;
163
- @body error: ApiError;
164
- } | {
165
- @statusCode statusCode: 404;
166
- @body error: NotFoundError;
167
- };
168
-
169
- @patch
170
- @route("/{id}")
171
- @useAuth(BearerAuth)
172
- @doc("Update a subject. Only provide the fields that need to be changed.")
173
- op update(
174
- @path accountId: Uuid,
175
- @path groupId: int64,
176
- @path id: int64,
177
- @body body: UpdateSubjectBody,
178
- ):
179
- | {
180
- @statusCode statusCode: 200;
181
- @body body: Subject;
182
- }
183
- | {
184
- @statusCode statusCode: 400;
185
- @body error: ValidationError;
186
- }
187
- | {
188
- @statusCode statusCode: 401;
189
- @body error: ApiError;
190
- }
191
- | {
192
- @statusCode statusCode: 404;
193
- @body error: NotFoundError;
194
- };
195
-
196
- @delete
197
- @route("/{id}")
198
- @useAuth(BearerAuth)
199
- @doc("Soft delete a subject.")
200
- op delete(@path accountId: Uuid, @path groupId: int64, @path id: int64): {
201
- @statusCode statusCode: 204;
202
- } | {
203
- @statusCode statusCode: 401;
204
- @body error: ApiError;
205
- } | {
206
- @statusCode statusCode: 404;
207
- @body error: NotFoundError;
208
- };
209
- }
210
-
211
- // ── NAMESPACE: SESSIONS ───────────────────────────────────────────────────
212
- // State machine:
213
- //
214
- // (none) ──start──► Starting ──pause──► Paused
215
- // ▲ │
216
- // └────resume─────────┘
217
- // │
218
- // stop
219
- // │
220
- // ▼
221
- // Stopped
222
- //
223
- // Each start/resume operation opens a new Timelog.
224
- // Each pause/stop operation closes the current Timelog (sets stoppedAt and calculates durationInSeconds).
225
-
226
- @route("/subjects/{subjectId}/sessions")
227
- @tag("Sessions")
228
- namespace Sessions {
229
- @post
230
- @useAuth(BearerAuth)
231
- @doc("""
232
- Start a new study session for the subject.
233
- The server creates a Log (status=Starting) and the first Timelog.
234
- Returns 409 if the subject already has a session in Starting or Paused status.
235
- """)
236
- op start(
237
- @path accountId: Uuid,
238
- @path subjectId: int64,
239
- @body body: SessionNoteBody,
240
- ):
241
- | {
242
- @statusCode statusCode: 201;
243
- @body body: SessionOpenedResponse;
244
- }
245
- | {
246
- @statusCode statusCode: 401;
247
- @body error: ApiError;
248
- }
249
- | {
250
- @statusCode statusCode: 404;
251
- @body error: NotFoundError;
252
- }
253
- | {
254
- @statusCode statusCode: 409;
255
-
256
- @doc("The subject already has an active session (status is Starting or Paused).")
257
- @body
258
- error: ConflictError;
259
- };
260
-
261
- @post
262
- @route("/{logId}/pause")
263
- @useAuth(BearerAuth)
264
- @doc("""
265
- Pause the currently running session (transition from Starting to Paused).
266
- The server closes the current Timelog (sets stoppedAt and calculates durationInSeconds).
267
- Returns 409 if the session is not in the Starting state.
268
- """)
269
- op pause(@path accountId: Uuid, @path subjectId: int64, @path logId: int64):
270
- | {
271
- @statusCode statusCode: 200;
272
- @body body: SessionClosedResponse;
273
- }
274
- | {
275
- @statusCode statusCode: 401;
276
- @body error: ApiError;
277
- }
278
- | {
279
- @statusCode statusCode: 404;
280
- @body error: NotFoundError;
281
- }
282
- | {
283
- @statusCode statusCode: 409;
284
-
285
- @doc("The session is not in the Starting state.")
286
- @body
287
- error: ConflictError;
288
- };
289
-
290
- @post
291
- @route("/{logId}/resume")
292
- @useAuth(BearerAuth)
293
- @doc("""
294
- Resume a paused session (transition from Paused to Starting).
295
- The server creates a new Timelog.
296
- Returns 409 if the session is not in the Paused state.
297
- """)
298
- op resume(@path accountId: Uuid, @path subjectId: int64, @path logId: int64):
299
- | {
300
- @statusCode statusCode: 200;
301
- @body body: SessionOpenedResponse;
302
- }
303
- | {
304
- @statusCode statusCode: 401;
305
- @body error: ApiError;
306
- }
307
- | {
308
- @statusCode statusCode: 404;
309
- @body error: NotFoundError;
310
- }
311
- | {
312
- @statusCode statusCode: 409;
313
-
314
- @doc("The session is not in the Paused state.")
315
- @body
316
- error: ConflictError;
317
- };
318
-
319
- @post
320
- @route("/{logId}/stop")
321
- @useAuth(BearerAuth)
322
- @doc("""
323
- End the session (transition from Starting or Paused to Stopped).
324
- The server closes the current Timelog if it is running, and updates Log.stoppedAt.
325
- Returns 409 if the session is already Stopped.
326
- """)
327
- op stop(
328
- @path accountId: Uuid,
329
- @path subjectId: int64,
330
- @path logId: int64,
331
- @body body: SessionNoteBody,
332
- ):
333
- | {
334
- @statusCode statusCode: 200;
335
- @body body: SessionClosedResponse;
336
- }
337
- | {
338
- @statusCode statusCode: 401;
339
- @body error: ApiError;
340
- }
341
- | {
342
- @statusCode statusCode: 404;
343
- @body error: NotFoundError;
344
- }
345
- | {
346
- @statusCode statusCode: 409;
347
-
348
- @doc("The session is already in the Stopped state.")
349
- @body
350
- error: ConflictError;
351
- };
352
- }
353
-
354
- @route("/accounts/{accountId}")
355
- @tag("Account")
356
- namespace Accounts {
357
- @get
358
- @route("/events")
359
- @doc("Server-Sent Events stream for learning data changes.")
360
- op events(
361
- @path accountId: Uuid,
362
- @header accept: string = "text/event-stream",
363
- ): SSEStream<LearningEvents>;
364
- }
@@ -1 +0,0 @@
1
- mod generated;