happy-rusty 1.5.0 → 1.6.0

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 (42) hide show
  1. package/CHANGELOG.md +206 -0
  2. package/README.cn.md +265 -19
  3. package/README.md +261 -21
  4. package/dist/main.cjs +382 -32
  5. package/dist/main.cjs.map +1 -1
  6. package/dist/main.mjs +374 -33
  7. package/dist/main.mjs.map +1 -1
  8. package/dist/types.d.ts +2002 -52
  9. package/package.json +37 -24
  10. package/dist/types.d.ts.map +0 -1
  11. package/docs/README.md +0 -47
  12. package/docs/functions/Err.md +0 -46
  13. package/docs/functions/Ok.md +0 -70
  14. package/docs/functions/Some.md +0 -45
  15. package/docs/functions/isOption.md +0 -35
  16. package/docs/functions/isResult.md +0 -36
  17. package/docs/functions/promiseToAsyncResult.md +0 -50
  18. package/docs/interfaces/None.md +0 -979
  19. package/docs/interfaces/Option.md +0 -857
  20. package/docs/interfaces/Result.md +0 -903
  21. package/docs/type-aliases/AsyncIOResult.md +0 -24
  22. package/docs/type-aliases/AsyncOption.md +0 -24
  23. package/docs/type-aliases/AsyncResult.md +0 -25
  24. package/docs/type-aliases/AsyncVoidIOResult.md +0 -17
  25. package/docs/type-aliases/AsyncVoidResult.md +0 -23
  26. package/docs/type-aliases/IOResult.md +0 -24
  27. package/docs/type-aliases/VoidIOResult.md +0 -17
  28. package/docs/type-aliases/VoidResult.md +0 -23
  29. package/docs/variables/None.md +0 -18
  30. package/docs/variables/RESULT_FALSE.md +0 -18
  31. package/docs/variables/RESULT_TRUE.md +0 -18
  32. package/docs/variables/RESULT_VOID.md +0 -17
  33. package/docs/variables/RESULT_ZERO.md +0 -18
  34. package/src/enum/constants.ts +0 -30
  35. package/src/enum/core.ts +0 -635
  36. package/src/enum/defines.ts +0 -45
  37. package/src/enum/extensions.ts +0 -31
  38. package/src/enum/mod.ts +0 -6
  39. package/src/enum/prelude.ts +0 -619
  40. package/src/enum/symbols.ts +0 -9
  41. package/src/enum/utils.ts +0 -27
  42. package/src/mod.ts +0 -1
package/README.md CHANGED
@@ -1,53 +1,293 @@
1
- # Use Rust features in JavaScript happily
1
+ # happy-rusty
2
+
3
+ Rust's `Option`, `Result`, and sync primitives for JavaScript/TypeScript - Better error handling and null-safety patterns.
2
4
 
3
5
  [![NPM version](https://img.shields.io/npm/v/happy-rusty.svg)](https://npmjs.org/package/happy-rusty)
4
6
  [![NPM downloads](https://badgen.net/npm/dm/happy-rusty)](https://npmjs.org/package/happy-rusty)
5
7
  [![JSR Version](https://jsr.io/badges/@happy-js/happy-rusty)](https://jsr.io/@happy-js/happy-rusty)
6
8
  [![JSR Score](https://jsr.io/badges/@happy-js/happy-rusty/score)](https://jsr.io/@happy-js/happy-rusty/score)
7
- [![Build Status](https://github.com/jiangjie/happy-rusty/actions/workflows/test.yml/badge.svg)](https://github.com/jiangjie/happy-rusty/actions/workflows/test.yml)
9
+ [![Build Status](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml/badge.svg)](https://github.com/JiangJie/happy-rusty/actions/workflows/test.yml)
8
10
  [![codecov](https://codecov.io/gh/JiangJie/happy-rusty/graph/badge.svg)](https://codecov.io/gh/JiangJie/happy-rusty)
9
11
 
10
12
  ---
11
13
 
12
- ## [中文](README.cn.md)
14
+ [中文](README.cn.md)
13
15
 
14
16
  ---
15
17
 
16
- ## Supported
18
+ ## Features
17
19
 
18
- - [Option](https://doc.rust-lang.org/core/option/index.html)
19
- - [Result](https://doc.rust-lang.org/core/result/index.html)
20
+ - **Option<T>** - Represents an optional value: every `Option` is either `Some(T)` or `None`
21
+ - **Result<T, E>** - Represents either success (`Ok(T)`) or failure (`Err(E)`)
22
+ - **Sync Primitives** - Rust-inspired `Once<T>`, `Lazy<T>`, `LazyAsync<T>`, and `Mutex<T>`
23
+ - **Control Flow** - `ControlFlow<B, C>` with `Break` and `Continue` for short-circuiting operations
24
+ - **Full TypeScript support** with strict type inference
25
+ - **Async support** - Async versions of all transformation methods
26
+ - **Zero dependencies**
27
+ - **Runtime immutability** - All instances are frozen with `Object.freeze()`
28
+ - **Cross-runtime** - Works in Node.js, Deno, Bun, and browsers
20
29
 
21
30
  ## Installation
22
31
 
23
32
  ```sh
24
- # via pnpm
25
- pnpm add happy-rusty
26
- # or via yarn
33
+ # npm
34
+ npm install happy-rusty
35
+
36
+ # yarn
27
37
  yarn add happy-rusty
28
- # or just from npm
29
- npm install --save happy-rusty
30
- # via JSR
31
- jsr add @happy-js/happy-rusty
32
- # for deno
38
+
39
+ # pnpm
40
+ pnpm add happy-rusty
41
+
42
+ # JSR (Deno)
33
43
  deno add @happy-js/happy-rusty
34
- # for bun
44
+
45
+ # JSR (Bun)
35
46
  bunx jsr add @happy-js/happy-rusty
36
47
  ```
37
48
 
38
- then import to your code.
49
+ ## Quick Start
39
50
 
40
51
  ```ts
41
52
  import { Some, None, Ok, Err } from 'happy-rusty';
53
+
54
+ // Option - handling nullable values
55
+ function findUser(id: number): Option<User> {
56
+ const user = database.get(id);
57
+ return user ? Some(user) : None;
58
+ }
59
+
60
+ const user = findUser(1)
61
+ .map(u => u.name)
62
+ .unwrapOr('Guest');
63
+
64
+ // Result - handling errors
65
+ function parseJSON<T>(json: string): Result<T, Error> {
66
+ try {
67
+ return Ok(JSON.parse(json));
68
+ } catch (e) {
69
+ return Err(e as Error);
70
+ }
71
+ }
72
+
73
+ const config = parseJSON<Config>(jsonStr)
74
+ .map(c => c.settings)
75
+ .unwrapOrElse(err => {
76
+ console.error('Parse failed:', err);
77
+ return defaultSettings;
78
+ });
79
+ ```
80
+
81
+ ## API Overview
82
+
83
+ ### Option&lt;T&gt;
84
+
85
+ | Category | Methods |
86
+ |----------|---------|
87
+ | **Constructors** | `Some(value)`, `None` |
88
+ | **Querying** | `isSome()`, `isNone()`, `isSomeAnd(fn)` |
89
+ | **Extracting** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
90
+ | **Transforming** | `map(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `filter(fn)`, `flatten()` |
91
+ | **Boolean ops** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)`, `xor(other)` |
92
+ | **Converting** | `okOr(err)`, `okOrElse(fn)`, `transpose()` |
93
+ | **Combining** | `zip(other)`, `zipWith(other, fn)`, `unzip()` |
94
+ | **Side effects** | `inspect(fn)` |
95
+ | **Comparison** | `eq(other)` |
96
+
97
+ ### Result&lt;T, E&gt;
98
+
99
+ | Category | Methods |
100
+ |----------|---------|
101
+ | **Constructors** | `Ok(value)`, `Ok()` (void), `Err(error)` |
102
+ | **Querying** | `isOk()`, `isErr()`, `isOkAnd(fn)`, `isErrAnd(fn)` |
103
+ | **Extracting Ok** | `expect(msg)`, `unwrap()`, `unwrapOr(default)`, `unwrapOrElse(fn)` |
104
+ | **Extracting Err** | `expectErr(msg)`, `unwrapErr()` |
105
+ | **Transforming** | `map(fn)`, `mapErr(fn)`, `mapOr(default, fn)`, `mapOrElse(defaultFn, fn)`, `flatten()` |
106
+ | **Boolean ops** | `and(other)`, `andThen(fn)`, `or(other)`, `orElse(fn)` |
107
+ | **Converting** | `ok()`, `err()`, `transpose()` |
108
+ | **Type casting** | `asOk<F>()`, `asErr<U>()` |
109
+ | **Side effects** | `inspect(fn)`, `inspectErr(fn)` |
110
+ | **Comparison** | `eq(other)` |
111
+
112
+ ### Async Methods
113
+
114
+ All transformation methods have async variants with `Async` suffix:
115
+
116
+ ```ts
117
+ // Async Option methods
118
+ isSomeAndAsync(asyncFn)
119
+ unwrapOrElseAsync(asyncFn)
120
+ andThenAsync(asyncFn)
121
+ orElseAsync(asyncFn)
122
+
123
+ // Async Result methods
124
+ isOkAndAsync(asyncFn)
125
+ isErrAndAsync(asyncFn)
126
+ unwrapOrElseAsync(asyncFn)
127
+ andThenAsync(asyncFn)
128
+ orElseAsync(asyncFn)
129
+ ```
130
+
131
+ ### Type Aliases
132
+
133
+ ```ts
134
+ // Convenient type aliases for common patterns
135
+ type AsyncOption<T> = Promise<Option<T>>;
136
+ type AsyncResult<T, E> = Promise<Result<T, E>>;
137
+
138
+ // For I/O operations
139
+ type IOResult<T> = Result<T, Error>;
140
+ type AsyncIOResult<T> = Promise<IOResult<T>>;
141
+
142
+ // For void returns
143
+ type VoidResult<E> = Result<void, E>;
144
+ type VoidIOResult = IOResult<void>;
145
+ type AsyncVoidResult<E> = Promise<VoidResult<E>>;
146
+ type AsyncVoidIOResult = Promise<VoidIOResult>;
42
147
  ```
43
148
 
44
- Enjoy the happiness.
149
+ ### Utility Functions
150
+
151
+ ```ts
152
+ import { isOption, isResult, isControlFlow, promiseToAsyncResult } from 'happy-rusty';
153
+
154
+ // Type guards
155
+ if (isOption(value)) { /* ... */ }
156
+ if (isResult(value)) { /* ... */ }
157
+ if (isControlFlow(value)) { /* ... */ }
158
+
159
+ // Convert Promise to Result
160
+ const result = await promiseToAsyncResult(fetch('/api/data'));
161
+ result.inspect(data => console.log(data))
162
+ .inspectErr(err => console.error(err));
163
+ ```
164
+
165
+ ### Constants
166
+
167
+ ```ts
168
+ import { RESULT_TRUE, RESULT_FALSE, RESULT_ZERO, RESULT_VOID } from 'happy-rusty';
45
169
 
46
- ## [Examples](examples/main.ts)
170
+ // Reusable immutable Result constants
171
+ function validate(): Result<boolean, Error> {
172
+ return isValid ? RESULT_TRUE : RESULT_FALSE;
173
+ }
47
174
 
48
- - [Option](examples/option.ts)
175
+ function doSomething(): Result<void, Error> {
176
+ // ...
177
+ return RESULT_VOID;
178
+ }
179
+ ```
180
+
181
+ ### Sync Primitives
182
+
183
+ ```ts
184
+ import { Once, Lazy, LazyAsync, Mutex } from 'happy-rusty';
185
+
186
+ // Once - one-time initialization (like Rust's OnceLock)
187
+ const config = Once<Config>();
188
+ config.set(loadConfig()); // Set once
189
+ config.get(); // Some(config) or None
190
+ config.getOrInit(() => defaultCfg); // Get or initialize
191
+
192
+ // Lazy - lazy initialization with initializer at construction
193
+ const expensive = Lazy(() => computeExpensiveValue());
194
+ expensive.force(); // Compute on first access, cached thereafter
195
+
196
+ // LazyAsync - async lazy initialization
197
+ const db = LazyAsync(async () => await Database.connect(url));
198
+ await db.force(); // Only one connection, concurrent calls wait
199
+
200
+ // Mutex - async mutual exclusion
201
+ const state = Mutex({ count: 0 });
202
+ await state.withLock(async (s) => {
203
+ s.count += 1; // Exclusive access
204
+ });
205
+ ```
206
+
207
+ ### Control Flow
208
+
209
+ ```ts
210
+ import { Break, Continue, ControlFlow } from 'happy-rusty';
211
+
212
+ // Short-circuit operations
213
+ function findFirst<T>(arr: T[], pred: (t: T) => boolean): Option<T> {
214
+ for (const item of arr) {
215
+ const flow = pred(item) ? Break(item) : Continue();
216
+ if (flow.isBreak()) {
217
+ return Some(flow.breakValue().unwrap());
218
+ }
219
+ }
220
+ return None;
221
+ }
222
+
223
+ // Custom fold with early exit
224
+ function tryFold<T, Acc>(
225
+ arr: T[],
226
+ init: Acc,
227
+ f: (acc: Acc, item: T) => ControlFlow<Acc, Acc>
228
+ ): Acc {
229
+ let acc = init;
230
+ for (const item of arr) {
231
+ const flow = f(acc, item);
232
+ if (flow.isBreak()) return flow.breakValue().unwrap();
233
+ acc = flow.continueValue().unwrap();
234
+ }
235
+ return acc;
236
+ }
237
+ ```
238
+
239
+ ## Examples
240
+
241
+ - [Option basics](examples/option.ts)
49
242
  - [AsyncOption](examples/option.async.ts)
50
- - [Result](examples/result.ts)
243
+ - [Result basics](examples/result.ts)
51
244
  - [AsyncResult](examples/result.async.ts)
245
+ - [Once](examples/once.ts)
246
+ - [Lazy](examples/lazy.ts)
247
+ - [Mutex](examples/mutex.ts)
248
+ - [ControlFlow](examples/control_flow.ts)
249
+
250
+ ## Documentation
251
+
252
+ Full API documentation is available at [jiangjie.github.io/happy-rusty](https://jiangjie.github.io/happy-rusty/).
253
+
254
+ ## Design Notes
255
+
256
+ ### Immutability
257
+
258
+ All types (`Option`, `Result`, `ControlFlow`, `Lazy`, `LazyAsync`, `Once`, `Mutex`, `MutexGuard`) are **immutable at runtime** via `Object.freeze()`. This prevents accidental modification of methods or properties:
259
+
260
+ ```ts
261
+ const some = Some(42);
262
+ some.unwrap = () => 0; // TypeError: Cannot assign to read only property
263
+ ```
264
+
265
+ **Why no `readonly` in TypeScript interfaces?**
266
+
267
+ We intentionally omit `readonly` modifiers from method signatures in interfaces. While this might seem to reduce type safety, there are compelling reasons:
268
+
269
+ 1. **Inheritance compatibility** - The `None` type extends `Option<never>`. TypeScript's arrow function property syntax (`readonly prop: () => T`) uses contravariant parameter checking, which causes `None` (with `never` parameters) to be incompatible with `Option<T>`. Method syntax (`method(): T`) uses bivariant checking, allowing the inheritance to work correctly.
270
+
271
+ 2. **Runtime protection is sufficient** - `Object.freeze()` already prevents reassignment at runtime. Adding `readonly` only provides compile-time checking, which offers marginal benefit when runtime protection exists.
272
+
273
+ 3. **Cleaner API** - Avoiding the `Mutable*` + `Readonly<>` pattern keeps the exported types clean and documentation readable.
274
+
275
+ 4. **Testing validates immutability** - Our test suite explicitly verifies that all instances are frozen and reject property modifications.
276
+
277
+ ## Why happy-rusty?
278
+
279
+ JavaScript's `null`/`undefined` and try-catch patterns lead to:
280
+ - Uncaught null reference errors
281
+ - Forgotten error handling
282
+ - Verbose try-catch blocks
283
+ - Unclear function contracts
284
+
285
+ `happy-rusty` provides Rust's battle-tested patterns:
286
+ - **Explicit optionality** - `Option<T>` makes absence visible in types
287
+ - **Explicit errors** - `Result<T, E>` forces error handling consideration
288
+ - **Method chaining** - Transform values without nested if-else or try-catch
289
+ - **Type safety** - Full TypeScript support with strict type inference
290
+
291
+ ## License
52
292
 
53
- ## [Docs](docs/README.md)
293
+ [GPL-3.0](LICENSE)