happy-rusty 1.9.2 → 1.10.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.
- package/CHANGELOG.md +82 -0
- package/README.cn.md +28 -24
- package/README.md +35 -26
- package/dist/{main.cjs → mod.cjs} +282 -66
- package/dist/mod.cjs.map +1 -0
- package/dist/mod.d.cts +5132 -0
- package/dist/mod.d.mts +5132 -0
- package/dist/{main.mjs → mod.mjs} +282 -67
- package/dist/mod.mjs.map +1 -0
- package/package.json +30 -30
- package/dist/main.cjs.map +0 -1
- package/dist/main.mjs.map +0 -1
- package/dist/types.d.ts +0 -4907
package/CHANGELOG.md
CHANGED
|
@@ -5,46 +5,83 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.10.1] - 2026-09-06
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Channel**: `for await` leaving early (a `break`, an error thrown in the loop body, or an explicit `iterator.return()`) now cancels the pending `next()`. An abandoned iterator no longer holds its spot in the receive queue, so it cannot swallow a message intended for another consumer
|
|
13
|
+
- **Channel**: a timeout callback firing after its waiter was already served no longer removes an unrelated waiter (`splice(-1, 1)` on a `-1` index)
|
|
14
|
+
- **OnceAsync**: `set()` and `tryInsert()` return `Err` while a `getOrInit`/`getOrTryInit` attempt is in flight, instead of reporting `Ok` and then being overwritten by the attempt's result — matching Rust's `OnceLock::set` racing an in-progress `get_or_init`
|
|
15
|
+
- **OnceAsync**: `wait()` no longer rejects when an initialization attempt fails. It keeps waiting until the cell is actually initialized, matching Rust's `OnceLock::wait`
|
|
16
|
+
- **OnceAsync**: a caller joining a failed attempt retries with its own initializer, matching Rust's `get_or_try_init`. The in-flight slot is claimed synchronously, so a synchronously-executing initializer that re-enters joins the attempt instead of starting a second one
|
|
17
|
+
|
|
18
|
+
### Chores
|
|
19
|
+
|
|
20
|
+
- Migrated to the Vite+ unified toolchain (`vp check` for fmt + lint + type checking, `vp pack` for JS and `.d.ts` bundling), replacing ESLint and the previous build setup
|
|
21
|
+
- Merged all CI gates into a single `ci.yml` job and added attw + publint verification of the tarball that is actually published
|
|
22
|
+
- Upgraded devDependencies: `vite-plus` 0.3.0, `@vitest/coverage-v8` 4.1.11, `publint` 0.3.24
|
|
23
|
+
|
|
24
|
+
## [1.10.0] - 2026-06-30
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **New Sync Primitive**: `Semaphore` - Counting semaphore for limiting async concurrency, with `acquire`/`tryAcquire`/`withPermit`/`availablePermits`. Inspired by tokio's `Semaphore` (Rust std does not include one). Useful for fetch rate limiting, connection pools, and task queues.
|
|
29
|
+
- **New Method**: `RwLockWriteGuard.downgrade()` - Atomically converts a write guard to a read guard, releasing waiting readers while keeping pending writers waiting. Equivalent to Rust 1.92's `RwLockWriteGuard::downgrade`
|
|
30
|
+
|
|
31
|
+
### Chores
|
|
32
|
+
|
|
33
|
+
- Upgraded devDependencies: vite, vitest, @vitest/coverage-v8, @vitest/ui, eslint, rollup, typescript-eslint
|
|
34
|
+
|
|
8
35
|
## [1.9.2] - 2026-04-12
|
|
9
36
|
|
|
10
37
|
### Changed
|
|
38
|
+
|
|
11
39
|
- **Performance**: Added `/*#__PURE__*/` annotations to all top-level function call expressions for better tree-shaking support in consumer bundlers
|
|
12
40
|
|
|
13
41
|
### Fixed
|
|
42
|
+
|
|
14
43
|
- **Build**: Migrated `treeshake` config from deprecated `'smallest'` string to object form for Vite 8 (Rolldown) compatibility
|
|
15
44
|
- **Build**: Added `topLevelVar: false` to output options to preserve `const` declarations in bundled output
|
|
16
45
|
|
|
17
46
|
### Chores
|
|
47
|
+
|
|
18
48
|
- Upgraded devDependencies: vite, vitest, eslint, typedoc, typescript-eslint
|
|
19
49
|
|
|
20
50
|
## [1.9.1] - 2026-01-16
|
|
21
51
|
|
|
22
52
|
### Changed
|
|
53
|
+
|
|
23
54
|
- **Performance**: `Channel` buffer replaced `Array` with `Queue` for O(1) shift operations
|
|
24
55
|
|
|
25
56
|
### Documentation
|
|
57
|
+
|
|
26
58
|
- Added `@since` tags to all public APIs
|
|
27
59
|
- Improved JSDoc consistency and formatting
|
|
28
60
|
|
|
29
61
|
### CI
|
|
62
|
+
|
|
30
63
|
- Changed JSR publish to trigger on release instead of push to main
|
|
31
64
|
|
|
32
65
|
## [1.9.0] - 2026-01-04
|
|
33
66
|
|
|
34
67
|
### Added
|
|
68
|
+
|
|
35
69
|
- **Try Extensions for Result**:
|
|
36
70
|
- `andTryAsync<U>` - Like `andThenAsync`, but auto-catches exceptions/Promise rejections and converts them to `Err`
|
|
37
71
|
- `orTryAsync<F>` - Like `orElseAsync`, but auto-catches exceptions in recovery logic
|
|
38
72
|
|
|
39
73
|
### Changed
|
|
74
|
+
|
|
40
75
|
- **Performance**: `map()` and `mapErr()` now return `this` when the Result variant doesn't match, avoiding unnecessary object creation
|
|
41
76
|
|
|
42
77
|
### Removed
|
|
78
|
+
|
|
43
79
|
- `promiseToAsyncResult()` - Use `tryAsyncResult()` instead (deprecated since v1.7.0)
|
|
44
80
|
|
|
45
81
|
## [1.8.0] - 2025-12-31
|
|
46
82
|
|
|
47
83
|
### Added
|
|
84
|
+
|
|
48
85
|
- **New Sync Primitives**:
|
|
49
86
|
- `OnceAsync<T>` - Async-first one-time initialization with concurrent call handling
|
|
50
87
|
- `RwLock<T>` - Async read-write lock (multiple concurrent readers or single writer)
|
|
@@ -69,18 +106,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
69
106
|
- **Rust Documentation Links** - All Rust-equivalent interfaces now link to official Rust docs via `@see` tags
|
|
70
107
|
|
|
71
108
|
### Changed
|
|
109
|
+
|
|
72
110
|
- **Module Reorganization** - Source code restructured into `core/` (Option, Result) and `std/` (sync, ops) directories, mirroring Rust's stdlib organization
|
|
73
111
|
- **Async Callback Types** - All async methods now accept `PromiseLike<T> | T` instead of just `Promise<T>`, allowing mixed sync/async callbacks
|
|
74
112
|
- **Async Return Types** - Use `Awaited<T>` to properly flatten nested Promises in async method return types
|
|
75
113
|
- **Internal Optimizations** - Cached Promise constants (`ASYNC_NONE`, etc.) for better runtime performance
|
|
76
114
|
|
|
77
115
|
### Fixed
|
|
116
|
+
|
|
78
117
|
- `this` binding issue in `Once.getOrTryInitAsync`
|
|
79
118
|
- Nested Promise flattening in async methods
|
|
80
119
|
|
|
81
120
|
## [1.7.1] - 2025-12-26
|
|
82
121
|
|
|
83
122
|
### Added
|
|
123
|
+
|
|
84
124
|
- **Argument passing** - All `try*` functions now support passing arguments like `Promise.try`:
|
|
85
125
|
- `tryOption(JSON.parse, jsonString)` instead of `tryOption(() => JSON.parse(jsonString))`
|
|
86
126
|
- `tryResult(decodeURIComponent, str)` instead of `tryResult(() => decodeURIComponent(str))`
|
|
@@ -89,35 +129,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
89
129
|
## [1.7.0] - 2025-12-26
|
|
90
130
|
|
|
91
131
|
### Added
|
|
132
|
+
|
|
92
133
|
- `tryOption()` - Capture synchronous exceptions as Option (success → Some, exception → None)
|
|
93
134
|
- `tryAsyncOption()` - Capture async/Promise exceptions as Option
|
|
94
135
|
- `tryResult()` - Capture synchronous exceptions as Result (success → Ok, exception → Err)
|
|
95
136
|
- `tryAsyncResult()` - Capture async/Promise exceptions as Result
|
|
96
137
|
|
|
97
138
|
### Deprecated
|
|
139
|
+
|
|
98
140
|
- `promiseToAsyncResult()` - Use `tryAsyncResult()` instead (same functionality, more consistent naming)
|
|
99
141
|
|
|
100
142
|
## [1.6.2] - 2025-12-25
|
|
101
143
|
|
|
102
144
|
### Changed
|
|
145
|
+
|
|
103
146
|
- `promiseToAsyncResult` now accepts `PromiseLike<T>` instead of `Promise<T>` for broader compatibility with thenable objects
|
|
104
147
|
- Use `AsyncResult` type alias in function signatures for consistency
|
|
105
148
|
|
|
106
149
|
### Removed
|
|
150
|
+
|
|
107
151
|
- Removed unused `source` field from package.json (Parcel legacy)
|
|
108
152
|
- Removed redundant `@internal` tags from private (non-exported) functions
|
|
109
153
|
|
|
110
154
|
## [1.6.1] - 2025-12-19
|
|
111
155
|
|
|
112
156
|
### Changed
|
|
157
|
+
|
|
113
158
|
- License changed from GPL-3.0 to MIT for broader adoption and easier integration
|
|
114
159
|
|
|
115
160
|
### Added
|
|
161
|
+
|
|
116
162
|
- Navigation links (GitHub, npm, JSR) in TypeDoc documentation header
|
|
117
163
|
|
|
118
164
|
## [1.6.0] - 2025-12-18
|
|
119
165
|
|
|
120
166
|
### Added
|
|
167
|
+
|
|
121
168
|
- **Sync Primitives**: `Once<T>`, `Lazy<T>`, `LazyAsync<T>`, `Mutex<T>` for Rust-style synchronization
|
|
122
169
|
- **Control Flow**: `ControlFlow<B, C>` with `Break` and `Continue` variants
|
|
123
170
|
- `Symbol.toStringTag` property to all types for better type identification
|
|
@@ -131,28 +178,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
131
178
|
- CHANGELOG.md with full version history
|
|
132
179
|
|
|
133
180
|
### Changed
|
|
181
|
+
|
|
134
182
|
- All instances are now frozen with `Object.freeze()` for runtime immutability
|
|
135
183
|
- TypeDoc output format from Markdown to HTML for GitHub Pages compatibility
|
|
136
184
|
- Migrated from Deno test to Vitest
|
|
137
185
|
- Build process split into Vite (JS) and Rollup (dts)
|
|
138
186
|
|
|
139
187
|
### Fixed
|
|
188
|
+
|
|
140
189
|
- Symbol property syntax and duplicate declaration issues
|
|
141
190
|
- Type inference improvements for `None` interface methods
|
|
142
191
|
|
|
143
192
|
## [1.5.0] - 2024-08-11
|
|
144
193
|
|
|
145
194
|
### Added
|
|
195
|
+
|
|
146
196
|
- Async versions of methods: `isSomeAndAsync`, `isOkAndAsync`, `isErrAndAsync`, `unwrapOrElseAsync`, `andThenAsync`, `orElseAsync`
|
|
147
197
|
- Async examples
|
|
148
198
|
|
|
149
199
|
### Changed
|
|
200
|
+
|
|
150
201
|
- Updated pnpm to v9.7.0
|
|
151
202
|
- Updated ESLint configuration
|
|
152
203
|
|
|
153
204
|
## [1.4.0] - 2024-08-05
|
|
154
205
|
|
|
155
206
|
### Added
|
|
207
|
+
|
|
156
208
|
- `Ok()` constructor without arguments (similar to Rust's `Ok(())`)
|
|
157
209
|
- `RESULT_VOID` constant for void Result returns
|
|
158
210
|
- `VoidResult<E>`, `VoidIOResult`, `AsyncVoidResult<E>`, `AsyncVoidIOResult` type aliases
|
|
@@ -160,94 +212,114 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
160
212
|
## [1.3.2] - 2024-08-04
|
|
161
213
|
|
|
162
214
|
### Changed
|
|
215
|
+
|
|
163
216
|
- Renamed `helpers` to `utils`
|
|
164
217
|
- Improved `@example` annotations in JSDoc
|
|
165
218
|
|
|
166
219
|
## [1.3.1] - 2024-08-03
|
|
167
220
|
|
|
168
221
|
### Fixed
|
|
222
|
+
|
|
169
223
|
- `map` methods now correctly return new objects instead of mutating
|
|
170
224
|
|
|
171
225
|
### Changed
|
|
226
|
+
|
|
172
227
|
- Updated Rollup to v4.20.0
|
|
173
228
|
|
|
174
229
|
## [1.3.0] - 2024-08-01
|
|
175
230
|
|
|
176
231
|
### Added
|
|
232
|
+
|
|
177
233
|
- `RESULT_TRUE`, `RESULT_FALSE`, `RESULT_ZERO` constants
|
|
178
234
|
|
|
179
235
|
### Fixed
|
|
236
|
+
|
|
180
237
|
- Circular dependency issues
|
|
181
238
|
|
|
182
239
|
### Changed
|
|
240
|
+
|
|
183
241
|
- Reorganized code structure
|
|
184
242
|
|
|
185
243
|
## [1.2.0] - 2024-08-01
|
|
186
244
|
|
|
187
245
|
### Added
|
|
246
|
+
|
|
188
247
|
- `isOption()` and `isResult()` type guard utilities
|
|
189
248
|
- Custom string conversion for Option and Result
|
|
190
249
|
|
|
191
250
|
### Changed
|
|
251
|
+
|
|
192
252
|
- Replaced arrow functions with normal functions for better debug experience
|
|
193
253
|
- Updated ESLint to v9
|
|
194
254
|
|
|
195
255
|
## [1.1.2] - 2024-07-17
|
|
196
256
|
|
|
197
257
|
### Added
|
|
258
|
+
|
|
198
259
|
- `asOk<F>()` and `asErr<U>()` methods for type casting
|
|
199
260
|
|
|
200
261
|
## [1.1.1] - 2024-07-13
|
|
201
262
|
|
|
202
263
|
### Fixed
|
|
264
|
+
|
|
203
265
|
- TypeDoc configuration issues
|
|
204
266
|
|
|
205
267
|
### Changed
|
|
268
|
+
|
|
206
269
|
- Updated dependencies
|
|
207
270
|
|
|
208
271
|
## [1.1.0] - 2024-06-09
|
|
209
272
|
|
|
210
273
|
### Added
|
|
274
|
+
|
|
211
275
|
- `Option.filter()` method
|
|
212
276
|
- Many new Option and Result APIs
|
|
213
277
|
- Comprehensive code comments and documentation
|
|
214
278
|
|
|
215
279
|
### Changed
|
|
280
|
+
|
|
216
281
|
- Improved type inference
|
|
217
282
|
- `Option.filter()` now returns `Option<T>` instead of boolean
|
|
218
283
|
|
|
219
284
|
## [1.0.9] - 2024-05-14
|
|
220
285
|
|
|
221
286
|
### Added
|
|
287
|
+
|
|
222
288
|
- Documentation for exported symbols
|
|
223
289
|
- README examples
|
|
224
290
|
|
|
225
291
|
## [1.0.8] - 2024-05-13
|
|
226
292
|
|
|
227
293
|
### Changed
|
|
294
|
+
|
|
228
295
|
- Switched from npm to pnpm
|
|
229
296
|
- Added GitHub Actions test workflow with Codecov
|
|
230
297
|
|
|
231
298
|
## [1.0.7] - 2024-05-08
|
|
232
299
|
|
|
233
300
|
### Changed
|
|
301
|
+
|
|
234
302
|
- `Some` value type changed to `NonNullable<T>`
|
|
235
303
|
|
|
236
304
|
### Fixed
|
|
305
|
+
|
|
237
306
|
- Can now invoke `err()` from `Ok` variant
|
|
238
307
|
|
|
239
308
|
## [1.0.6] - 2024-05-08
|
|
240
309
|
|
|
241
310
|
### Added
|
|
311
|
+
|
|
242
312
|
- Commonly used type exports
|
|
243
313
|
|
|
244
314
|
### Changed
|
|
315
|
+
|
|
245
316
|
- Set `type: "module"` in package.json
|
|
246
317
|
- Build target set to ESNext
|
|
247
318
|
|
|
248
319
|
## [1.0.5] - 2024-05-05
|
|
249
320
|
|
|
250
321
|
### Changed
|
|
322
|
+
|
|
251
323
|
- Throw `TypeError` instead of generic `Error`
|
|
252
324
|
- Switched to Rollup for building
|
|
253
325
|
- Added Bun support in CI
|
|
@@ -255,45 +327,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
255
327
|
## [1.0.4] - 2024-05-04
|
|
256
328
|
|
|
257
329
|
### Added
|
|
330
|
+
|
|
258
331
|
- Chinese README (`README.cn.md`)
|
|
259
332
|
- JSR publishing workflow
|
|
260
333
|
- npm publishing workflows
|
|
261
334
|
|
|
262
335
|
### Changed
|
|
336
|
+
|
|
263
337
|
- Replaced Parcel with Rollup for building
|
|
264
338
|
- Replaced Jest with Bun test
|
|
265
339
|
|
|
266
340
|
## [1.0.3] - 2024-04-27
|
|
267
341
|
|
|
268
342
|
### Added
|
|
343
|
+
|
|
269
344
|
- Installation and Examples sections in README
|
|
270
345
|
|
|
271
346
|
### Changed
|
|
347
|
+
|
|
272
348
|
- Force return const for better type inference
|
|
273
349
|
|
|
274
350
|
## [1.0.2] - 2024-04-26
|
|
275
351
|
|
|
276
352
|
### Added
|
|
353
|
+
|
|
277
354
|
- JSR publishing support
|
|
278
355
|
|
|
279
356
|
### Changed
|
|
357
|
+
|
|
280
358
|
- Improved code comments
|
|
281
359
|
|
|
282
360
|
## [1.0.1] - 2024-04-26
|
|
283
361
|
|
|
284
362
|
### Changed
|
|
363
|
+
|
|
285
364
|
- Replaced enum with const for better tree-shaking
|
|
286
365
|
- Marked package as side-effect free
|
|
287
366
|
|
|
288
367
|
## [1.0.0] - 2024-04-24
|
|
289
368
|
|
|
290
369
|
### Added
|
|
370
|
+
|
|
291
371
|
- Initial release
|
|
292
372
|
- `Option<T>` type with `Some` and `None` variants
|
|
293
373
|
- `Result<T, E>` type with `Ok` and `Err` variants
|
|
294
374
|
- Full TypeScript support
|
|
295
375
|
- Comprehensive API matching Rust's Option and Result
|
|
296
376
|
|
|
377
|
+
[1.10.1]: https://github.com/JiangJie/happy-rusty/compare/v1.10.0...v1.10.1
|
|
378
|
+
[1.10.0]: https://github.com/JiangJie/happy-rusty/compare/v1.9.2...v1.10.0
|
|
297
379
|
[1.9.2]: https://github.com/JiangJie/happy-rusty/compare/v1.9.1...v1.9.2
|
|
298
380
|
[1.9.1]: https://github.com/JiangJie/happy-rusty/compare/v1.9.0...v1.9.1
|
|
299
381
|
[1.9.0]: https://github.com/JiangJie/happy-rusty/compare/v1.8.0...v1.9.0
|
package/README.cn.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# happy-rusty
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
|
-
[](https://github.com/JiangJie/happy-rusty/actions/workflows/ci.yml)
|
|
5
5
|
[](https://codecov.io/gh/JiangJie/happy-rusty)
|
|
6
6
|
[](https://npmjs.org/package/happy-rusty)
|
|
7
7
|
[](https://npmjs.org/package/happy-rusty)
|
|
@@ -84,37 +84,39 @@ const config = parseJSON<Config>(jsonStr)
|
|
|
84
84
|
|
|
85
85
|
### Option<T>
|
|
86
86
|
|
|
87
|
-
| 分类
|
|
88
|
-
|
|
89
|
-
| **构造器**
|
|
90
|
-
| **查询**
|
|
91
|
-
| **提取**
|
|
92
|
-
| **转换**
|
|
93
|
-
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)`
|
|
94
|
-
| **类型转换** | `okOr(err)`, `okOrElse(fn)`, `transpose()`
|
|
95
|
-
| **组合**
|
|
96
|
-
| **副作用**
|
|
97
|
-
| **比较**
|
|
87
|
+
| 分类 | 方法 |
|
|
88
|
+
| ------------ | -------------------------------------------------------------------------------------- |
|
|
89
|
+
| **构造器** | `Some(value)`, `None` |
|
|
90
|
+
| **查询** | `isSome()`, `isNone()`, `isSomeAnd(fn)` |
|
|
91
|
+
| **提取** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
92
|
+
| **转换** | `map(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `filter(fn)`, `flatten()` |
|
|
93
|
+
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)` |
|
|
94
|
+
| **类型转换** | `okOr(err)`, `okOrElse(fn)`, `transpose()` |
|
|
95
|
+
| **组合** | `zip(other)`, `zipWith(other, fn)`, `unzip()` |
|
|
96
|
+
| **副作用** | `inspect(fn)` |
|
|
97
|
+
| **比较** | `eq(other)` |
|
|
98
98
|
|
|
99
99
|
### Result<T, E>
|
|
100
100
|
|
|
101
|
-
| 分类
|
|
102
|
-
|
|
103
|
-
| **构造器**
|
|
104
|
-
| **查询**
|
|
105
|
-
| **提取 Ok**
|
|
106
|
-
| **提取 Err** | `expectErr(msg)`, `unwrapErr()`
|
|
107
|
-
| **转换**
|
|
108
|
-
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`
|
|
109
|
-
| **类型转换** | `ok()`, `err()`, `transpose()`
|
|
110
|
-
| **类型断言** | `asOk<F>()`, `asErr<U>()`
|
|
111
|
-
| **副作用**
|
|
112
|
-
| **比较**
|
|
101
|
+
| 分类 | 方法 |
|
|
102
|
+
| ------------ | -------------------------------------------------------------------------------------- |
|
|
103
|
+
| **构造器** | `Ok(value)`, `Ok()` (void), `Err(error)` |
|
|
104
|
+
| **查询** | `isOk()`, `isErr()`, `isOkAnd(fn)`, `isErrAnd(fn)` |
|
|
105
|
+
| **提取 Ok** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
106
|
+
| **提取 Err** | `expectErr(msg)`, `unwrapErr()` |
|
|
107
|
+
| **转换** | `map(fn)`, `mapErr(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `flatten()` |
|
|
108
|
+
| **布尔操作** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)` |
|
|
109
|
+
| **类型转换** | `ok()`, `err()`, `transpose()` |
|
|
110
|
+
| **类型断言** | `asOk<F>()`, `asErr<U>()` |
|
|
111
|
+
| **副作用** | `inspect(fn)`, `inspectErr(fn)` |
|
|
112
|
+
| **比较** | `eq(other)` |
|
|
113
113
|
|
|
114
114
|
### 异步方法
|
|
115
115
|
|
|
116
116
|
所有转换方法都有带 `Async` 后缀的异步变体(如 `andThenAsync`、`mapAsync`、`unwrapOrElseAsync`)。
|
|
117
117
|
|
|
118
|
+
异步变体遵循库的错误约定:回调被实际调用时,若其同步抛错,错误会在调用点同步抛出——与同步方法一致;只有回调返回 rejected Promise 时,错误才以 rejection 形式呈现。如需把抛出的异常转为 `Err`/`None` 值,请使用 `try*` 辅助函数(`tryResult`/`tryAsyncResult`/`tryOption`/`tryAsyncOption`),或在 `Result` 上使用 `andTryAsync`/`orTryAsync` 方法。
|
|
119
|
+
|
|
118
120
|
### 类型别名
|
|
119
121
|
|
|
120
122
|
```ts
|
|
@@ -195,12 +197,14 @@ some.unwrap = () => 0; // TypeError: Cannot assign to read only property
|
|
|
195
197
|
## 为什么选择 happy-rusty?
|
|
196
198
|
|
|
197
199
|
JavaScript 的 `null`/`undefined` 和 try-catch 模式会导致:
|
|
200
|
+
|
|
198
201
|
- 未捕获的空引用错误
|
|
199
202
|
- 遗忘的错误处理
|
|
200
203
|
- 冗长的 try-catch 代码块
|
|
201
204
|
- 不清晰的函数契约
|
|
202
205
|
|
|
203
206
|
`happy-rusty` 提供了 Rust 久经考验的模式:
|
|
207
|
+
|
|
204
208
|
- **显式可选性** - `Option<T>` 在类型中明确表示值的缺失
|
|
205
209
|
- **显式错误** - `Result<T, E>` 强制考虑错误处理
|
|
206
210
|
- **链式调用** - 无需嵌套 if-else 或 try-catch 即可转换值
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# happy-rusty
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
|
-
[](https://github.com/JiangJie/happy-rusty/actions/workflows/ci.yml)
|
|
5
5
|
[](https://codecov.io/gh/JiangJie/happy-rusty)
|
|
6
6
|
[](https://npmjs.org/package/happy-rusty)
|
|
7
7
|
[](https://npmjs.org/package/happy-rusty)
|
|
@@ -20,7 +20,7 @@ Rust's `Option`, `Result`, and sync primitives for JavaScript/TypeScript - Bette
|
|
|
20
20
|
|
|
21
21
|
- **Option<T>** - Represents an optional value: every `Option` is either `Some(T)` or `None`
|
|
22
22
|
- **Result<T, E>** - Represents either success (`Ok(T)`) or failure (`Err(E)`)
|
|
23
|
-
- **Sync Primitives** - Rust-inspired `Once<T>`, `OnceAsync<T>`, `Lazy<T>`, `LazyAsync<T>`, `Mutex<T>`, `RwLock<T>`, and `Channel<T>`
|
|
23
|
+
- **Sync Primitives** - Rust-inspired `Once<T>`, `OnceAsync<T>`, `Lazy<T>`, `LazyAsync<T>`, `Mutex<T>`, `RwLock<T>`, `Semaphore`, and `Channel<T>`
|
|
24
24
|
- **Control Flow** - `ControlFlow<B, C>` with `Break` and `Continue` for short-circuiting operations
|
|
25
25
|
- **FnOnce** - One-time callable function wrappers (`FnOnce` and `FnOnceAsync`)
|
|
26
26
|
- **Full TypeScript support** with strict type inference
|
|
@@ -84,37 +84,39 @@ const config = parseJSON<Config>(jsonStr)
|
|
|
84
84
|
|
|
85
85
|
### Option<T>
|
|
86
86
|
|
|
87
|
-
| Category
|
|
88
|
-
|
|
89
|
-
| **Constructors** | `Some(value)`, `None`
|
|
90
|
-
| **Querying**
|
|
91
|
-
| **Extracting**
|
|
87
|
+
| Category | Methods |
|
|
88
|
+
| ---------------- | -------------------------------------------------------------------------------------- |
|
|
89
|
+
| **Constructors** | `Some(value)`, `None` |
|
|
90
|
+
| **Querying** | `isSome()`, `isNone()`, `isSomeAnd(fn)` |
|
|
91
|
+
| **Extracting** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
92
92
|
| **Transforming** | `map(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `filter(fn)`, `flatten()` |
|
|
93
|
-
| **Boolean ops**
|
|
94
|
-
| **Converting**
|
|
95
|
-
| **Combining**
|
|
96
|
-
| **Side effects** | `inspect(fn)`
|
|
97
|
-
| **Comparison**
|
|
93
|
+
| **Boolean ops** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)` |
|
|
94
|
+
| **Converting** | `okOr(err)`, `okOrElse(fn)`, `transpose()` |
|
|
95
|
+
| **Combining** | `zip(other)`, `zipWith(other, fn)`, `unzip()` |
|
|
96
|
+
| **Side effects** | `inspect(fn)` |
|
|
97
|
+
| **Comparison** | `eq(other)` |
|
|
98
98
|
|
|
99
99
|
### Result<T, E>
|
|
100
100
|
|
|
101
|
-
| Category
|
|
102
|
-
|
|
103
|
-
| **Constructors**
|
|
104
|
-
| **Querying**
|
|
105
|
-
| **Extracting Ok**
|
|
106
|
-
| **Extracting Err** | `expectErr(msg)`, `unwrapErr()`
|
|
107
|
-
| **Transforming**
|
|
108
|
-
| **Boolean ops**
|
|
109
|
-
| **Converting**
|
|
110
|
-
| **Type casting**
|
|
111
|
-
| **Side effects**
|
|
112
|
-
| **Comparison**
|
|
101
|
+
| Category | Methods |
|
|
102
|
+
| ------------------ | -------------------------------------------------------------------------------------- |
|
|
103
|
+
| **Constructors** | `Ok(value)`, `Ok()` (void), `Err(error)` |
|
|
104
|
+
| **Querying** | `isOk()`, `isErr()`, `isOkAnd(fn)`, `isErrAnd(fn)` |
|
|
105
|
+
| **Extracting Ok** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
|
|
106
|
+
| **Extracting Err** | `expectErr(msg)`, `unwrapErr()` |
|
|
107
|
+
| **Transforming** | `map(fn)`, `mapErr(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `flatten()` |
|
|
108
|
+
| **Boolean ops** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)` |
|
|
109
|
+
| **Converting** | `ok()`, `err()`, `transpose()` |
|
|
110
|
+
| **Type casting** | `asOk<F>()`, `asErr<U>()` |
|
|
111
|
+
| **Side effects** | `inspect(fn)`, `inspectErr(fn)` |
|
|
112
|
+
| **Comparison** | `eq(other)` |
|
|
113
113
|
|
|
114
114
|
### Async Methods
|
|
115
115
|
|
|
116
116
|
All transformation methods have async variants with `Async` suffix (e.g., `andThenAsync`, `mapAsync`, `unwrapOrElseAsync`).
|
|
117
117
|
|
|
118
|
+
Async variants follow the library's error convention: when the callback is invoked, a synchronous throw propagates synchronously at the call site — just like the sync methods — and only a callback that returns a rejected promise surfaces as a promise rejection. To capture thrown exceptions as `Err`/`None` values instead, use the `try*` helpers (`tryResult`/`tryAsyncResult`/`tryOption`/`tryAsyncOption`) or, on `Result`, the `andTryAsync`/`orTryAsync` methods.
|
|
119
|
+
|
|
118
120
|
### Type Aliases
|
|
119
121
|
|
|
120
122
|
```ts
|
|
@@ -137,7 +139,7 @@ const response = await tryAsyncResult(fetch, '/api/data');
|
|
|
137
139
|
### Sync Primitives
|
|
138
140
|
|
|
139
141
|
```ts
|
|
140
|
-
import { Lazy, LazyAsync, Mutex, Channel } from 'happy-rusty';
|
|
142
|
+
import { Lazy, LazyAsync, Mutex, Semaphore, Channel } from 'happy-rusty';
|
|
141
143
|
|
|
142
144
|
// Lazy - compute once on first access
|
|
143
145
|
const expensive = Lazy(() => computeExpensiveValue());
|
|
@@ -151,6 +153,10 @@ await db.force(); // Only one connection, concurrent calls wait
|
|
|
151
153
|
const state = Mutex({ count: 0 });
|
|
152
154
|
await state.withLock(async (s) => { s.count += 1; });
|
|
153
155
|
|
|
156
|
+
// Semaphore - limit async concurrency (e.g. fetch rate limiting)
|
|
157
|
+
const sem = Semaphore(10);
|
|
158
|
+
await sem.withPermit(() => fetch(url));
|
|
159
|
+
|
|
154
160
|
// Channel - MPMC async message passing
|
|
155
161
|
const ch = Channel<string>(10); // bounded capacity
|
|
156
162
|
await ch.send('hello');
|
|
@@ -165,6 +171,7 @@ for await (const msg of ch) { console.log(msg); }
|
|
|
165
171
|
- [Lazy](examples/std/sync/lazy.ts) / [LazyAsync](examples/std/sync/lazy_async.ts)
|
|
166
172
|
- [Mutex](examples/std/sync/mutex.ts)
|
|
167
173
|
- [RwLock](examples/std/sync/rwlock.ts)
|
|
174
|
+
- [Semaphore](examples/std/sync/semaphore.ts)
|
|
168
175
|
- [Channel](examples/std/sync/channel.ts)
|
|
169
176
|
- [ControlFlow](examples/std/ops/control_flow.ts)
|
|
170
177
|
- [FnOnce](examples/std/ops/fn_once.ts) / [FnOnceAsync](examples/std/ops/fn_once_async.ts)
|
|
@@ -173,7 +180,7 @@ for await (const msg of ch) { console.log(msg); }
|
|
|
173
180
|
|
|
174
181
|
### Immutability
|
|
175
182
|
|
|
176
|
-
All types (`Option`, `Result`, `ControlFlow`, `Lazy`, `LazyAsync`, `Once`, `OnceAsync`, `Mutex`, `MutexGuard`, `RwLock`, `Channel`, `Sender`, `Receiver`, `FnOnce`, `FnOnceAsync`) are **immutable at runtime** via `Object.freeze()`. This prevents accidental modification of methods or properties:
|
|
183
|
+
All types (`Option`, `Result`, `ControlFlow`, `Lazy`, `LazyAsync`, `Once`, `OnceAsync`, `Mutex`, `MutexGuard`, `RwLock`, `Semaphore`, `SemaphorePermit`, `Channel`, `Sender`, `Receiver`, `FnOnce`, `FnOnceAsync`) are **immutable at runtime** via `Object.freeze()`. This prevents accidental modification of methods or properties:
|
|
177
184
|
|
|
178
185
|
```ts
|
|
179
186
|
const some = Some(42);
|
|
@@ -195,12 +202,14 @@ We intentionally omit `readonly` modifiers from method signatures in interfaces.
|
|
|
195
202
|
## Why happy-rusty?
|
|
196
203
|
|
|
197
204
|
JavaScript's `null`/`undefined` and try-catch patterns lead to:
|
|
205
|
+
|
|
198
206
|
- Uncaught null reference errors
|
|
199
207
|
- Forgotten error handling
|
|
200
208
|
- Verbose try-catch blocks
|
|
201
209
|
- Unclear function contracts
|
|
202
210
|
|
|
203
211
|
`happy-rusty` provides Rust's battle-tested patterns:
|
|
212
|
+
|
|
204
213
|
- **Explicit optionality** - `Option<T>` makes absence visible in types
|
|
205
214
|
- **Explicit errors** - `Result<T, E>` forces error handling consideration
|
|
206
215
|
- **Method chaining** - Transform values without nested if-else or try-catch
|