@akiojin/gwt 6.30.2 → 9.0.1

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.
Files changed (98) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.claude-plugin/marketplace.json +18 -0
  3. package/.coderabbit.yaml +8 -0
  4. package/.codex/skills/gwt-fix-issue/scripts/inspect_issue.py +833 -0
  5. package/.dockerignore +63 -0
  6. package/.gitattributes +27 -0
  7. package/.husky/commit-msg +2 -0
  8. package/.husky/pre-commit +9 -0
  9. package/.husky/pre-push +12 -0
  10. package/.markdownlint.json +18 -0
  11. package/.markdownlintignore +2 -0
  12. package/Dockerfile +58 -0
  13. package/README.ja.md +161 -484
  14. package/README.md +164 -444
  15. package/cliff.toml +56 -0
  16. package/clippy.toml +2 -0
  17. package/cmake/ci-disable-native.cmake +16 -0
  18. package/codecov.yml +16 -0
  19. package/commitlint.config.cjs +107 -0
  20. package/deny.toml +35 -0
  21. package/docker-compose.yml +59 -0
  22. package/messages/errors.toml +52 -0
  23. package/package.json +12 -22
  24. package/rustfmt.toml +8 -0
  25. package/scripts/check-e2e-coverage-threshold.mjs +238 -0
  26. package/scripts/entrypoint.sh +36 -25
  27. package/scripts/install-linux-deps.sh +46 -0
  28. package/scripts/postinstall.js +79 -227
  29. package/scripts/release_issue_refs.py +317 -0
  30. package/scripts/run-local-backend-tests-on-commit.sh +15 -0
  31. package/scripts/run-local-e2e-coverage-on-commit.sh +69 -0
  32. package/scripts/run-local-e2e-on-commit.sh +60 -0
  33. package/scripts/test-all.sh +13 -0
  34. package/scripts/test_release_issue_refs.py +257 -0
  35. package/scripts/validate-skill-frontmatter.sh +108 -0
  36. package/scripts/verify-ci-node-toolchain.sh +76 -0
  37. package/scripts/verify-husky-hooks.sh +6 -0
  38. package/scripts/voice-eval.sh +48 -0
  39. package/tests/voice_eval/README.md +53 -0
  40. package/tests/voice_eval/manifest.template.json +55 -0
  41. package/tests/voice_eval/samples/.gitkeep +1 -0
  42. package/tests/voice_eval/script-ja.txt +10 -0
  43. package/vendor/ratatui-core/src/backend/test.rs +1077 -0
  44. package/vendor/ratatui-core/src/backend.rs +405 -0
  45. package/vendor/ratatui-core/src/buffer/assert.rs +71 -0
  46. package/vendor/ratatui-core/src/buffer/buffer.rs +1388 -0
  47. package/vendor/ratatui-core/src/buffer/cell.rs +377 -0
  48. package/vendor/ratatui-core/src/buffer.rs +9 -0
  49. package/vendor/ratatui-core/src/layout/alignment.rs +89 -0
  50. package/vendor/ratatui-core/src/layout/constraint.rs +526 -0
  51. package/vendor/ratatui-core/src/layout/direction.rs +63 -0
  52. package/vendor/ratatui-core/src/layout/flex.rs +212 -0
  53. package/vendor/ratatui-core/src/layout/layout.rs +2838 -0
  54. package/vendor/ratatui-core/src/layout/margin.rs +79 -0
  55. package/vendor/ratatui-core/src/layout/offset.rs +66 -0
  56. package/vendor/ratatui-core/src/layout/position.rs +253 -0
  57. package/vendor/ratatui-core/src/layout/rect/iter.rs +356 -0
  58. package/vendor/ratatui-core/src/layout/rect/ops.rs +136 -0
  59. package/vendor/ratatui-core/src/layout/rect.rs +1114 -0
  60. package/vendor/ratatui-core/src/layout/size.rs +147 -0
  61. package/vendor/ratatui-core/src/layout.rs +333 -0
  62. package/vendor/ratatui-core/src/lib.rs +82 -0
  63. package/vendor/ratatui-core/src/style/anstyle.rs +348 -0
  64. package/vendor/ratatui-core/src/style/color.rs +788 -0
  65. package/vendor/ratatui-core/src/style/palette/material.rs +608 -0
  66. package/vendor/ratatui-core/src/style/palette/tailwind.rs +653 -0
  67. package/vendor/ratatui-core/src/style/palette.rs +6 -0
  68. package/vendor/ratatui-core/src/style/palette_conversion.rs +82 -0
  69. package/vendor/ratatui-core/src/style/stylize.rs +668 -0
  70. package/vendor/ratatui-core/src/style.rs +1069 -0
  71. package/vendor/ratatui-core/src/symbols/bar.rs +51 -0
  72. package/vendor/ratatui-core/src/symbols/block.rs +51 -0
  73. package/vendor/ratatui-core/src/symbols/border.rs +709 -0
  74. package/vendor/ratatui-core/src/symbols/braille.rs +21 -0
  75. package/vendor/ratatui-core/src/symbols/half_block.rs +3 -0
  76. package/vendor/ratatui-core/src/symbols/line.rs +259 -0
  77. package/vendor/ratatui-core/src/symbols/marker.rs +82 -0
  78. package/vendor/ratatui-core/src/symbols/merge.rs +748 -0
  79. package/vendor/ratatui-core/src/symbols/pixel.rs +30 -0
  80. package/vendor/ratatui-core/src/symbols/scrollbar.rs +46 -0
  81. package/vendor/ratatui-core/src/symbols/shade.rs +5 -0
  82. package/vendor/ratatui-core/src/symbols.rs +15 -0
  83. package/vendor/ratatui-core/src/terminal/frame.rs +192 -0
  84. package/vendor/ratatui-core/src/terminal/terminal.rs +926 -0
  85. package/vendor/ratatui-core/src/terminal/viewport.rs +58 -0
  86. package/vendor/ratatui-core/src/terminal.rs +40 -0
  87. package/vendor/ratatui-core/src/text/grapheme.rs +84 -0
  88. package/vendor/ratatui-core/src/text/line.rs +1678 -0
  89. package/vendor/ratatui-core/src/text/masked.rs +149 -0
  90. package/vendor/ratatui-core/src/text/span.rs +904 -0
  91. package/vendor/ratatui-core/src/text/text.rs +1434 -0
  92. package/vendor/ratatui-core/src/text.rs +64 -0
  93. package/vendor/ratatui-core/src/widgets/stateful_widget.rs +193 -0
  94. package/vendor/ratatui-core/src/widgets/widget.rs +174 -0
  95. package/vendor/ratatui-core/src/widgets.rs +9 -0
  96. package/bin/gwt.js +0 -131
  97. package/scripts/postinstall.test.js +0 -71
  98. package/scripts/release-download.js +0 -66
@@ -0,0 +1,356 @@
1
+ use crate::layout::{Position, Rect};
2
+
3
+ /// An iterator over rows within a `Rect`.
4
+ pub struct Rows {
5
+ /// The `Rect` associated with the rows.
6
+ rect: Rect,
7
+ /// The y coordinate of the row within the `Rect` when iterating forwards.
8
+ current_row_fwd: u16,
9
+ /// The y coordinate of the row within the `Rect` when iterating backwards.
10
+ current_row_back: u16,
11
+ }
12
+
13
+ impl Rows {
14
+ /// Creates a new `Rows` iterator.
15
+ pub const fn new(rect: Rect) -> Self {
16
+ Self {
17
+ rect,
18
+ current_row_fwd: rect.y,
19
+ current_row_back: rect.bottom(),
20
+ }
21
+ }
22
+ }
23
+
24
+ impl Iterator for Rows {
25
+ type Item = Rect;
26
+
27
+ /// Retrieves the next row within the `Rect`.
28
+ ///
29
+ /// Returns `None` when there are no more rows to iterate through.
30
+ fn next(&mut self) -> Option<Self::Item> {
31
+ if self.current_row_fwd >= self.current_row_back {
32
+ return None;
33
+ }
34
+ let row = Rect::new(self.rect.x, self.current_row_fwd, self.rect.width, 1);
35
+ self.current_row_fwd += 1;
36
+ Some(row)
37
+ }
38
+
39
+ fn size_hint(&self) -> (usize, Option<usize>) {
40
+ let start_count = self.current_row_fwd.saturating_sub(self.rect.top());
41
+ let end_count = self.rect.bottom().saturating_sub(self.current_row_back);
42
+ let count = self
43
+ .rect
44
+ .height
45
+ .saturating_sub(start_count)
46
+ .saturating_sub(end_count) as usize;
47
+ (count, Some(count))
48
+ }
49
+ }
50
+
51
+ impl DoubleEndedIterator for Rows {
52
+ /// Retrieves the previous row within the `Rect`.
53
+ ///
54
+ /// Returns `None` when there are no more rows to iterate through.
55
+ fn next_back(&mut self) -> Option<Self::Item> {
56
+ if self.current_row_back <= self.current_row_fwd {
57
+ return None;
58
+ }
59
+ self.current_row_back -= 1;
60
+ let row = Rect::new(self.rect.x, self.current_row_back, self.rect.width, 1);
61
+ Some(row)
62
+ }
63
+ }
64
+
65
+ /// An iterator over columns within a `Rect`.
66
+ pub struct Columns {
67
+ /// The `Rect` associated with the columns.
68
+ rect: Rect,
69
+ /// The x coordinate of the column within the `Rect` when iterating forwards.
70
+ current_column_fwd: u16,
71
+ /// The x coordinate of the column within the `Rect` when iterating backwards.
72
+ current_column_back: u16,
73
+ }
74
+
75
+ impl Columns {
76
+ /// Creates a new `Columns` iterator.
77
+ pub const fn new(rect: Rect) -> Self {
78
+ Self {
79
+ rect,
80
+ current_column_fwd: rect.x,
81
+ current_column_back: rect.right(),
82
+ }
83
+ }
84
+ }
85
+
86
+ impl Iterator for Columns {
87
+ type Item = Rect;
88
+
89
+ /// Retrieves the next column within the `Rect`.
90
+ ///
91
+ /// Returns `None` when there are no more columns to iterate through.
92
+ fn next(&mut self) -> Option<Self::Item> {
93
+ if self.current_column_fwd >= self.current_column_back {
94
+ return None;
95
+ }
96
+ let column = Rect::new(self.current_column_fwd, self.rect.y, 1, self.rect.height);
97
+ self.current_column_fwd += 1;
98
+ Some(column)
99
+ }
100
+
101
+ fn size_hint(&self) -> (usize, Option<usize>) {
102
+ let start_count = self.current_column_fwd.saturating_sub(self.rect.left());
103
+ let end_count = self.rect.right().saturating_sub(self.current_column_back);
104
+ let count = self
105
+ .rect
106
+ .width
107
+ .saturating_sub(start_count)
108
+ .saturating_sub(end_count) as usize;
109
+ (count, Some(count))
110
+ }
111
+ }
112
+
113
+ impl DoubleEndedIterator for Columns {
114
+ /// Retrieves the previous column within the `Rect`.
115
+ ///
116
+ /// Returns `None` when there are no more columns to iterate through.
117
+ fn next_back(&mut self) -> Option<Self::Item> {
118
+ if self.current_column_back <= self.current_column_fwd {
119
+ return None;
120
+ }
121
+ self.current_column_back -= 1;
122
+ let column = Rect::new(self.current_column_back, self.rect.y, 1, self.rect.height);
123
+ Some(column)
124
+ }
125
+ }
126
+
127
+ /// An iterator over positions within a `Rect`.
128
+ ///
129
+ /// The iterator will yield all positions within the `Rect` in a row-major order.
130
+ pub struct Positions {
131
+ /// The `Rect` associated with the positions.
132
+ rect: Rect,
133
+ /// The current position within the `Rect`.
134
+ current_position: Position,
135
+ }
136
+
137
+ impl Positions {
138
+ /// Creates a new `Positions` iterator.
139
+ pub const fn new(rect: Rect) -> Self {
140
+ Self {
141
+ rect,
142
+ current_position: Position::new(rect.x, rect.y),
143
+ }
144
+ }
145
+ }
146
+
147
+ impl Iterator for Positions {
148
+ type Item = Position;
149
+
150
+ /// Retrieves the next position within the `Rect`.
151
+ ///
152
+ /// Returns `None` when there are no more positions to iterate through.
153
+ fn next(&mut self) -> Option<Self::Item> {
154
+ if !self.rect.contains(self.current_position) {
155
+ return None;
156
+ }
157
+ let position = self.current_position;
158
+ self.current_position.x += 1;
159
+ if self.current_position.x >= self.rect.right() {
160
+ self.current_position.x = self.rect.x;
161
+ self.current_position.y += 1;
162
+ }
163
+ Some(position)
164
+ }
165
+
166
+ fn size_hint(&self) -> (usize, Option<usize>) {
167
+ let row_count = self.rect.bottom().saturating_sub(self.current_position.y);
168
+ if row_count == 0 {
169
+ return (0, Some(0));
170
+ }
171
+ let column_count = self.rect.right().saturating_sub(self.current_position.x);
172
+ // subtract 1 from the row count to account for the current row
173
+ let count = (row_count - 1)
174
+ .saturating_mul(self.rect.width)
175
+ .saturating_add(column_count) as usize;
176
+ (count, Some(count))
177
+ }
178
+ }
179
+
180
+ #[cfg(test)]
181
+ mod tests {
182
+ use super::*;
183
+
184
+ #[test]
185
+ fn rows() {
186
+ let rect = Rect::new(0, 0, 2, 3);
187
+ let mut rows = Rows::new(rect);
188
+ assert_eq!(rows.size_hint(), (3, Some(3)));
189
+ assert_eq!(rows.next(), Some(Rect::new(0, 0, 2, 1)));
190
+ assert_eq!(rows.size_hint(), (2, Some(2)));
191
+ assert_eq!(rows.next(), Some(Rect::new(0, 1, 2, 1)));
192
+ assert_eq!(rows.size_hint(), (1, Some(1)));
193
+ assert_eq!(rows.next(), Some(Rect::new(0, 2, 2, 1)));
194
+ assert_eq!(rows.size_hint(), (0, Some(0)));
195
+ assert_eq!(rows.next(), None);
196
+ assert_eq!(rows.size_hint(), (0, Some(0)));
197
+ assert_eq!(rows.next_back(), None);
198
+ assert_eq!(rows.size_hint(), (0, Some(0)));
199
+ }
200
+
201
+ #[test]
202
+ fn rows_back() {
203
+ let rect = Rect::new(0, 0, 2, 3);
204
+ let mut rows = Rows::new(rect);
205
+ assert_eq!(rows.size_hint(), (3, Some(3)));
206
+ assert_eq!(rows.next_back(), Some(Rect::new(0, 2, 2, 1)));
207
+ assert_eq!(rows.size_hint(), (2, Some(2)));
208
+ assert_eq!(rows.next_back(), Some(Rect::new(0, 1, 2, 1)));
209
+ assert_eq!(rows.size_hint(), (1, Some(1)));
210
+ assert_eq!(rows.next_back(), Some(Rect::new(0, 0, 2, 1)));
211
+ assert_eq!(rows.size_hint(), (0, Some(0)));
212
+ assert_eq!(rows.next_back(), None);
213
+ assert_eq!(rows.size_hint(), (0, Some(0)));
214
+ assert_eq!(rows.next(), None);
215
+ assert_eq!(rows.size_hint(), (0, Some(0)));
216
+ }
217
+
218
+ #[test]
219
+ fn rows_meet_in_the_middle() {
220
+ let rect = Rect::new(0, 0, 2, 4);
221
+ let mut rows = Rows::new(rect);
222
+ assert_eq!(rows.size_hint(), (4, Some(4)));
223
+ assert_eq!(rows.next(), Some(Rect::new(0, 0, 2, 1)));
224
+ assert_eq!(rows.size_hint(), (3, Some(3)));
225
+ assert_eq!(rows.next_back(), Some(Rect::new(0, 3, 2, 1)));
226
+ assert_eq!(rows.size_hint(), (2, Some(2)));
227
+ assert_eq!(rows.next(), Some(Rect::new(0, 1, 2, 1)));
228
+ assert_eq!(rows.size_hint(), (1, Some(1)));
229
+ assert_eq!(rows.next_back(), Some(Rect::new(0, 2, 2, 1)));
230
+ assert_eq!(rows.size_hint(), (0, Some(0)));
231
+ assert_eq!(rows.next(), None);
232
+ assert_eq!(rows.size_hint(), (0, Some(0)));
233
+ assert_eq!(rows.next_back(), None);
234
+ assert_eq!(rows.size_hint(), (0, Some(0)));
235
+ }
236
+
237
+ #[test]
238
+ fn columns() {
239
+ let rect = Rect::new(0, 0, 3, 2);
240
+ let mut columns = Columns::new(rect);
241
+ assert_eq!(columns.size_hint(), (3, Some(3)));
242
+ assert_eq!(columns.next(), Some(Rect::new(0, 0, 1, 2)));
243
+ assert_eq!(columns.size_hint(), (2, Some(2)));
244
+ assert_eq!(columns.next(), Some(Rect::new(1, 0, 1, 2)));
245
+ assert_eq!(columns.size_hint(), (1, Some(1)));
246
+ assert_eq!(columns.next(), Some(Rect::new(2, 0, 1, 2)));
247
+ assert_eq!(columns.size_hint(), (0, Some(0)));
248
+ assert_eq!(columns.next(), None);
249
+ assert_eq!(columns.size_hint(), (0, Some(0)));
250
+ assert_eq!(columns.next_back(), None);
251
+ assert_eq!(columns.size_hint(), (0, Some(0)));
252
+ }
253
+
254
+ #[test]
255
+ fn columns_back() {
256
+ let rect = Rect::new(0, 0, 3, 2);
257
+ let mut columns = Columns::new(rect);
258
+ assert_eq!(columns.size_hint(), (3, Some(3)));
259
+ assert_eq!(columns.next_back(), Some(Rect::new(2, 0, 1, 2)));
260
+ assert_eq!(columns.size_hint(), (2, Some(2)));
261
+ assert_eq!(columns.next_back(), Some(Rect::new(1, 0, 1, 2)));
262
+ assert_eq!(columns.size_hint(), (1, Some(1)));
263
+ assert_eq!(columns.next_back(), Some(Rect::new(0, 0, 1, 2)));
264
+ assert_eq!(columns.size_hint(), (0, Some(0)));
265
+ assert_eq!(columns.next_back(), None);
266
+ assert_eq!(columns.size_hint(), (0, Some(0)));
267
+ assert_eq!(columns.next(), None);
268
+ assert_eq!(columns.size_hint(), (0, Some(0)));
269
+ }
270
+
271
+ #[test]
272
+ fn columns_meet_in_the_middle() {
273
+ let rect = Rect::new(0, 0, 4, 2);
274
+ let mut columns = Columns::new(rect);
275
+ assert_eq!(columns.size_hint(), (4, Some(4)));
276
+ assert_eq!(columns.next(), Some(Rect::new(0, 0, 1, 2)));
277
+ assert_eq!(columns.size_hint(), (3, Some(3)));
278
+ assert_eq!(columns.next_back(), Some(Rect::new(3, 0, 1, 2)));
279
+ assert_eq!(columns.size_hint(), (2, Some(2)));
280
+ assert_eq!(columns.next(), Some(Rect::new(1, 0, 1, 2)));
281
+ assert_eq!(columns.size_hint(), (1, Some(1)));
282
+ assert_eq!(columns.next_back(), Some(Rect::new(2, 0, 1, 2)));
283
+ assert_eq!(columns.size_hint(), (0, Some(0)));
284
+ assert_eq!(columns.next(), None);
285
+ assert_eq!(columns.size_hint(), (0, Some(0)));
286
+ assert_eq!(columns.next_back(), None);
287
+ assert_eq!(columns.size_hint(), (0, Some(0)));
288
+ }
289
+
290
+ /// We allow a total of `65536` columns in the range `(0..=65535)`. In this test we iterate
291
+ /// forward and skip the first `65534` columns, and expect the next column to be `65535` and
292
+ /// the subsequent columns to be `None`.
293
+ #[test]
294
+ fn columns_max() {
295
+ let rect = Rect::new(0, 0, u16::MAX, 1);
296
+ let mut columns = Columns::new(rect).skip(usize::from(u16::MAX - 1));
297
+ assert_eq!(columns.next(), Some(Rect::new(u16::MAX - 1, 0, 1, 1)));
298
+ assert_eq!(columns.next(), None);
299
+ }
300
+
301
+ /// We allow a total of `65536` columns in the range `(0..=65535)`. In this test we iterate
302
+ /// backward and skip the last `65534` columns, and expect the next column to be `0` and the
303
+ /// subsequent columns to be `None`.
304
+ #[test]
305
+ fn columns_min() {
306
+ let rect = Rect::new(0, 0, u16::MAX, 1);
307
+ let mut columns = Columns::new(rect).rev().skip(usize::from(u16::MAX - 1));
308
+ assert_eq!(columns.next(), Some(Rect::new(0, 0, 1, 1)));
309
+ assert_eq!(columns.next(), None);
310
+ assert_eq!(columns.next(), None);
311
+ }
312
+
313
+ #[test]
314
+ fn positions() {
315
+ let rect = Rect::new(0, 0, 2, 2);
316
+ let mut positions = Positions::new(rect);
317
+ assert_eq!(positions.size_hint(), (4, Some(4)));
318
+ assert_eq!(positions.next(), Some(Position::new(0, 0)));
319
+ assert_eq!(positions.size_hint(), (3, Some(3)));
320
+ assert_eq!(positions.next(), Some(Position::new(1, 0)));
321
+ assert_eq!(positions.size_hint(), (2, Some(2)));
322
+ assert_eq!(positions.next(), Some(Position::new(0, 1)));
323
+ assert_eq!(positions.size_hint(), (1, Some(1)));
324
+ assert_eq!(positions.next(), Some(Position::new(1, 1)));
325
+ assert_eq!(positions.size_hint(), (0, Some(0)));
326
+ assert_eq!(positions.next(), None);
327
+ assert_eq!(positions.size_hint(), (0, Some(0)));
328
+ }
329
+
330
+ #[test]
331
+ fn positions_zero_width() {
332
+ let rect = Rect::new(0, 0, 0, 1);
333
+ let mut positions = Positions::new(rect);
334
+ assert_eq!(positions.size_hint(), (0, Some(0)));
335
+ assert_eq!(positions.next(), None);
336
+ assert_eq!(positions.size_hint(), (0, Some(0)));
337
+ }
338
+
339
+ #[test]
340
+ fn positions_zero_height() {
341
+ let rect = Rect::new(0, 0, 1, 0);
342
+ let mut positions = Positions::new(rect);
343
+ assert_eq!(positions.size_hint(), (0, Some(0)));
344
+ assert_eq!(positions.next(), None);
345
+ assert_eq!(positions.size_hint(), (0, Some(0)));
346
+ }
347
+
348
+ #[test]
349
+ fn positions_zero_by_zero() {
350
+ let rect = Rect::new(0, 0, 0, 0);
351
+ let mut positions = Positions::new(rect);
352
+ assert_eq!(positions.size_hint(), (0, Some(0)));
353
+ assert_eq!(positions.next(), None);
354
+ assert_eq!(positions.size_hint(), (0, Some(0)));
355
+ }
356
+ }
@@ -0,0 +1,136 @@
1
+ use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
2
+
3
+ use super::{Offset, Rect};
4
+
5
+ impl Neg for Offset {
6
+ type Output = Self;
7
+
8
+ /// Negates the offset.
9
+ ///
10
+ /// # Panics
11
+ ///
12
+ /// Panics if the negated value overflows (i.e. `x` or `y` is `i32::MIN`).
13
+ fn neg(self) -> Self {
14
+ Self {
15
+ x: self.x.neg(),
16
+ y: self.y.neg(),
17
+ }
18
+ }
19
+ }
20
+
21
+ impl Add<Offset> for Rect {
22
+ type Output = Self;
23
+
24
+ /// Moves the rect by an offset without changing its size.
25
+ ///
26
+ /// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
27
+ /// rect's position is clamped to the nearest edge.
28
+ fn add(self, offset: Offset) -> Self {
29
+ let max_x = i32::from(u16::MAX - self.width);
30
+ let max_y = i32::from(u16::MAX - self.height);
31
+ let x = i32::from(self.x).saturating_add(offset.x).clamp(0, max_x) as u16;
32
+ let y = i32::from(self.y).saturating_add(offset.y).clamp(0, max_y) as u16;
33
+ Self { x, y, ..self }
34
+ }
35
+ }
36
+
37
+ impl Add<Rect> for Offset {
38
+ type Output = Rect;
39
+
40
+ /// Moves the rect by an offset without changing its size.
41
+ ///
42
+ /// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
43
+ /// rect's position is clamped to the nearest edge.
44
+ fn add(self, rect: Rect) -> Rect {
45
+ rect + self
46
+ }
47
+ }
48
+
49
+ impl Sub<Offset> for Rect {
50
+ type Output = Self;
51
+
52
+ /// Subtracts an offset from the rect without changing its size.
53
+ ///
54
+ /// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
55
+ /// rect's position is clamped to the nearest
56
+ fn sub(self, offset: Offset) -> Self {
57
+ // Note this cannot be simplified to `self + -offset` because `Offset::MIN` would overflow
58
+ let max_x = i32::from(u16::MAX - self.width);
59
+ let max_y = i32::from(u16::MAX - self.height);
60
+ let x = i32::from(self.x).saturating_sub(offset.x).clamp(0, max_x) as u16;
61
+ let y = i32::from(self.y).saturating_sub(offset.y).clamp(0, max_y) as u16;
62
+ Self { x, y, ..self }
63
+ }
64
+ }
65
+
66
+ impl AddAssign<Offset> for Rect {
67
+ /// Moves the rect by an offset in place without changing its size.
68
+ ///
69
+ /// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
70
+ /// rect's position is clamped to the nearest edge.
71
+ fn add_assign(&mut self, offset: Offset) {
72
+ *self = *self + offset;
73
+ }
74
+ }
75
+
76
+ impl SubAssign<Offset> for Rect {
77
+ /// Moves the rect by an offset in place without changing its size.
78
+ ///
79
+ /// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
80
+ /// rect's position is clamped to the nearest edge.
81
+ fn sub_assign(&mut self, offset: Offset) {
82
+ *self = *self - offset;
83
+ }
84
+ }
85
+
86
+ #[cfg(test)]
87
+ mod tests {
88
+ use rstest::rstest;
89
+
90
+ use super::*;
91
+
92
+ #[rstest]
93
+ #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
94
+ #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(4, 6, 5, 6))]
95
+ #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(2, 2, 5, 6))]
96
+ #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MIN, Rect::new(0, 0, 5, 6))]
97
+ #[case::saturate_positive(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(u16::MAX- 5, u16::MAX - 6, 5, 6))]
98
+ fn add_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
99
+ assert_eq!(rect + offset, expected);
100
+ assert_eq!(offset + rect, expected);
101
+ }
102
+
103
+ #[rstest]
104
+ #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
105
+ #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(2, 2, 5, 6))]
106
+ #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(4, 6, 5, 6))]
107
+ #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(0, 0, 5, 6))]
108
+ #[case::saturate_positive(Rect::new(3, 4, 5, 6), -Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
109
+ fn sub_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
110
+ assert_eq!(rect - offset, expected);
111
+ }
112
+
113
+ #[rstest]
114
+ #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
115
+ #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(4, 6, 5, 6))]
116
+ #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(2, 2, 5, 6))]
117
+ #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MIN, Rect::new(0, 0, 5, 6))]
118
+ #[case::saturate_positive(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
119
+ fn add_assign_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
120
+ let mut rect = rect;
121
+ rect += offset;
122
+ assert_eq!(rect, expected);
123
+ }
124
+
125
+ #[rstest]
126
+ #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
127
+ #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(2, 2, 5, 6))]
128
+ #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(4, 6, 5, 6))]
129
+ #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(0, 0, 5, 6))]
130
+ #[case::saturate_positive(Rect::new(3, 4, 5, 6), -Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
131
+ fn sub_assign_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
132
+ let mut rect = rect;
133
+ rect -= offset;
134
+ assert_eq!(rect, expected);
135
+ }
136
+ }