ashlr-plugin 1.36.4

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 (499) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +39 -0
  3. package/.claude-plugin/plugin.json +36 -0
  4. package/.codex/agents/README.md +9 -0
  5. package/.codex/agents/ashlr-explorer.md +36 -0
  6. package/.codex/agents/ashlr-worker.md +38 -0
  7. package/.codex-plugin/plugin.json +45 -0
  8. package/.mcp.json +14 -0
  9. package/LICENSE +21 -0
  10. package/README.md +311 -0
  11. package/agents/ashlr-code.md +69 -0
  12. package/agents/ashlr-explore.md +44 -0
  13. package/agents/ashlr-plan.md +54 -0
  14. package/bench/fixtures/notebook-large.ipynb +660 -0
  15. package/bench/fixtures/notebook-small.ipynb +39 -0
  16. package/bench/fixtures/tasklist-closed.json +62 -0
  17. package/bench/fixtures/tasklist-large-mixed.json +27 -0
  18. package/bench/fixtures/tasklist-sprint-open.json +79 -0
  19. package/bench/fixtures/websearch-bun-runtime.json +32 -0
  20. package/bench/fixtures/websearch-react-hooks-patterns.json +32 -0
  21. package/bench/fixtures/websearch-typescript-generics.json +38 -0
  22. package/bench/refs/node-sdk/.refrev +1 -0
  23. package/bench/refs/node-sdk/README.md +17 -0
  24. package/bench/refs/node-sdk/src/agent/agent.ts +163 -0
  25. package/bench/refs/node-sdk/src/agent/create-agent-ui-stream.ts +83 -0
  26. package/bench/refs/node-sdk/src/agent/tool-loop-agent.ts +238 -0
  27. package/bench/refs/node-sdk/src/embed/embed-many.ts +409 -0
  28. package/bench/refs/node-sdk/src/embed/embed-result.ts +50 -0
  29. package/bench/refs/node-sdk/src/embed/embed.ts +257 -0
  30. package/bench/refs/node-sdk/src/error/invalid-argument-error.ts +34 -0
  31. package/bench/refs/node-sdk/src/error/invalid-tool-input-error.ts +33 -0
  32. package/bench/refs/node-sdk/src/error/no-object-generated-error.ts +70 -0
  33. package/bench/refs/node-sdk/src/generate-image/generate-image-result.ts +42 -0
  34. package/bench/refs/node-sdk/src/generate-image/generate-image.ts +362 -0
  35. package/bench/refs/node-sdk/src/generate-object/generate-object-result.ts +67 -0
  36. package/bench/refs/node-sdk/src/generate-object/generate-object.ts +501 -0
  37. package/bench/refs/node-sdk/src/generate-object/inject-json-instruction.ts +30 -0
  38. package/bench/refs/node-sdk/src/generate-object/output-strategy.ts +415 -0
  39. package/bench/refs/node-sdk/src/generate-object/repair-text.ts +12 -0
  40. package/bench/refs/node-sdk/src/generate-object/stream-object.ts +960 -0
  41. package/bench/refs/node-sdk/src/generate-speech/generate-speech.ts +191 -0
  42. package/bench/refs/node-sdk/src/generate-text/generate-text.ts +1484 -0
  43. package/bench/refs/node-sdk/src/generate-text/stream-text.ts +2708 -0
  44. package/bench/refs/node-sdk/src/generate-text/tool-call.ts +59 -0
  45. package/bench/refs/node-sdk/src/generate-text/tool-result.ts +36 -0
  46. package/bench/refs/node-sdk/src/telemetry/create-telemetry-dispatcher.ts +146 -0
  47. package/bench/refs/node-sdk/src/telemetry/telemetry-registry.ts +15 -0
  48. package/bench/refs/node-sdk/src/telemetry/telemetry.ts +223 -0
  49. package/bench/refs/node-sdk/src/util/async-iterable-stream.ts +94 -0
  50. package/bench/refs/node-sdk/src/util/consume-stream.ts +31 -0
  51. package/bench/refs/node-sdk/src/util/create-stitchable-stream.ts +112 -0
  52. package/bench/refs/node-sdk/src/util/download/download.ts +74 -0
  53. package/bench/refs/node-sdk/src/util/error-handler.ts +1 -0
  54. package/bench/refs/node-sdk/src/util/fix-json.ts +401 -0
  55. package/bench/refs/node-sdk/src/util/merge-objects.ts +79 -0
  56. package/bench/refs/python-lib/.refrev +1 -0
  57. package/bench/refs/python-lib/README.md +18 -0
  58. package/bench/refs/python-lib/pandas/core/_numba/executor.py +249 -0
  59. package/bench/refs/python-lib/pandas/core/_numba/extensions.py +586 -0
  60. package/bench/refs/python-lib/pandas/core/_numba/kernels/mean_.py +198 -0
  61. package/bench/refs/python-lib/pandas/core/_numba/kernels/min_max_.py +178 -0
  62. package/bench/refs/python-lib/pandas/core/_numba/kernels/sum_.py +255 -0
  63. package/bench/refs/python-lib/pandas/core/_numba/kernels/var_.py +251 -0
  64. package/bench/refs/python-lib/pandas/core/accessor.py +520 -0
  65. package/bench/refs/python-lib/pandas/core/api.py +138 -0
  66. package/bench/refs/python-lib/pandas/core/array_algos/masked_accumulations.py +97 -0
  67. package/bench/refs/python-lib/pandas/core/array_algos/masked_reductions.py +252 -0
  68. package/bench/refs/python-lib/pandas/core/array_algos/putmask.py +150 -0
  69. package/bench/refs/python-lib/pandas/core/array_algos/quantile.py +232 -0
  70. package/bench/refs/python-lib/pandas/core/array_algos/replace.py +159 -0
  71. package/bench/refs/python-lib/pandas/core/array_algos/take.py +609 -0
  72. package/bench/refs/python-lib/pandas/core/arraylike.py +534 -0
  73. package/bench/refs/python-lib/pandas/core/arrays/_arrow_string_mixins.py +434 -0
  74. package/bench/refs/python-lib/pandas/core/arrays/_mixins.py +630 -0
  75. package/bench/refs/python-lib/pandas/core/arrays/_ranges.py +276 -0
  76. package/bench/refs/python-lib/pandas/core/arrays/_utils.py +77 -0
  77. package/bench/refs/python-lib/pandas/core/arrays/arrow/accessors.py +521 -0
  78. package/bench/refs/python-lib/pandas/core/arrays/arrow/extension_types.py +174 -0
  79. package/bench/refs/python-lib/pandas/core/arrays/boolean.py +454 -0
  80. package/bench/refs/python-lib/pandas/core/arrays/floating.py +192 -0
  81. package/bench/refs/python-lib/pandas/core/arrays/integer.py +296 -0
  82. package/bench/refs/python-lib/pandas/core/arrays/masked.py +2140 -0
  83. package/bench/refs/python-lib/pandas/core/arrays/numeric.py +305 -0
  84. package/bench/refs/python-lib/pandas/core/arrays/numpy_.py +701 -0
  85. package/bench/refs/python-lib/pandas/core/arrays/period.py +1586 -0
  86. package/bench/refs/python-lib/pandas/core/arrays/sparse/accessor.py +505 -0
  87. package/bench/refs/python-lib/pandas/core/arrays/sparse/scipy_sparse.py +208 -0
  88. package/bench/refs/rust-project/.refrev +1 -0
  89. package/bench/refs/rust-project/README.md +17 -0
  90. package/bench/refs/rust-project/tokio/src/doc/os.rs +68 -0
  91. package/bench/refs/rust-project/tokio/src/fs/dir_builder.rs +129 -0
  92. package/bench/refs/rust-project/tokio/src/fs/file.rs +1113 -0
  93. package/bench/refs/rust-project/tokio/src/fs/mocks.rs +176 -0
  94. package/bench/refs/rust-project/tokio/src/fs/mod.rs +324 -0
  95. package/bench/refs/rust-project/tokio/src/fs/open_options.rs +856 -0
  96. package/bench/refs/rust-project/tokio/src/fs/read.rs +80 -0
  97. package/bench/refs/rust-project/tokio/src/fs/read_dir.rs +357 -0
  98. package/bench/refs/rust-project/tokio/src/fs/write.rs +99 -0
  99. package/bench/refs/rust-project/tokio/src/future/maybe_done.rs +123 -0
  100. package/bench/refs/rust-project/tokio/src/future/try_join.rs +82 -0
  101. package/bench/refs/rust-project/tokio/src/io/async_buf_read.rs +117 -0
  102. package/bench/refs/rust-project/tokio/src/io/async_read.rs +133 -0
  103. package/bench/refs/rust-project/tokio/src/io/async_seek.rs +96 -0
  104. package/bench/refs/rust-project/tokio/src/io/async_write.rs +402 -0
  105. package/bench/refs/rust-project/tokio/src/io/blocking.rs +339 -0
  106. package/bench/refs/rust-project/tokio/src/io/interest.rs +345 -0
  107. package/bench/refs/rust-project/tokio/src/io/join.rs +130 -0
  108. package/bench/refs/rust-project/tokio/src/io/mod.rs +308 -0
  109. package/bench/refs/rust-project/tokio/src/io/poll_evented.rs +319 -0
  110. package/bench/refs/rust-project/tokio/src/io/read_buf.rs +344 -0
  111. package/bench/refs/rust-project/tokio/src/io/ready.rs +322 -0
  112. package/bench/refs/rust-project/tokio/src/io/split.rs +166 -0
  113. package/bench/refs/rust-project/tokio/src/io/stderr.rs +148 -0
  114. package/bench/refs/rust-project/tokio/src/io/stdin.rs +98 -0
  115. package/bench/refs/rust-project/tokio/src/io/stdio_common.rs +222 -0
  116. package/bench/refs/rust-project/tokio/src/io/stdout.rs +197 -0
  117. package/bench/refs/rust-project/tokio/src/io/util/buf_reader.rs +311 -0
  118. package/bench/refs/rust-project/tokio/src/io/util/buf_stream.rs +207 -0
  119. package/bench/refs/rust-project/tokio/src/io/util/buf_writer.rs +310 -0
  120. package/bench/refs/rust-project/tokio/src/io/util/chain.rs +144 -0
  121. package/commands/_spawn-patterns.json +32 -0
  122. package/commands/ashlr-allow.md +21 -0
  123. package/commands/ashlr-benchmark.md +53 -0
  124. package/commands/ashlr-brief.md +97 -0
  125. package/commands/ashlr-budget.md +76 -0
  126. package/commands/ashlr-compact.md +54 -0
  127. package/commands/ashlr-dashboard.md +37 -0
  128. package/commands/ashlr-doctor.md +56 -0
  129. package/commands/ashlr-eco-mode.md +92 -0
  130. package/commands/ashlr-genome-init.md +24 -0
  131. package/commands/ashlr-genome-keygen.md +36 -0
  132. package/commands/ashlr-genome-loop.md +118 -0
  133. package/commands/ashlr-genome-push.md +36 -0
  134. package/commands/ashlr-genome-rewrap.md +48 -0
  135. package/commands/ashlr-genome-team-init.md +67 -0
  136. package/commands/ashlr-handoff.md +23 -0
  137. package/commands/ashlr-help.md +86 -0
  138. package/commands/ashlr-hook-timings.md +30 -0
  139. package/commands/ashlr-leaderboard.md +50 -0
  140. package/commands/ashlr-ollama-setup.md +39 -0
  141. package/commands/ashlr-orchestrate-status.md +72 -0
  142. package/commands/ashlr-orchestrate.md +83 -0
  143. package/commands/ashlr-parallelize.md +63 -0
  144. package/commands/ashlr-report-crash.md +33 -0
  145. package/commands/ashlr-resume.md +72 -0
  146. package/commands/ashlr-savings.md +24 -0
  147. package/commands/ashlr-settings.md +50 -0
  148. package/commands/ashlr-spawn.md +61 -0
  149. package/commands/ashlr-start.md +60 -0
  150. package/commands/ashlr-status.md +64 -0
  151. package/commands/ashlr-team-invite.md +47 -0
  152. package/commands/ashlr-tier.md +122 -0
  153. package/commands/ashlr-tour.md +60 -0
  154. package/commands/ashlr-update.md +155 -0
  155. package/commands/ashlr-upgrade.md +96 -0
  156. package/commands/goal.md +58 -0
  157. package/commands/loop.md +40 -0
  158. package/docs/CNAME +1 -0
  159. package/docs/PRO_TIER.md +104 -0
  160. package/docs/README.md +21 -0
  161. package/docs/architecture.md +415 -0
  162. package/docs/ashlr-pipe.md +161 -0
  163. package/docs/assets/logo.svg +19 -0
  164. package/docs/assets/og.png +0 -0
  165. package/docs/assets/og.svg +41 -0
  166. package/docs/benchmarks-v2.json +355 -0
  167. package/docs/benchmarks.json +102 -0
  168. package/docs/benchmarks.md +211 -0
  169. package/docs/billing.md +185 -0
  170. package/docs/cloud-genome.md +155 -0
  171. package/docs/competitive-wozcode.md +92 -0
  172. package/docs/deploy.md +325 -0
  173. package/docs/email-templates.md +113 -0
  174. package/docs/embed-tuning.md +103 -0
  175. package/docs/genome.md +120 -0
  176. package/docs/github-oauth-onboarding.md +127 -0
  177. package/docs/hero-video-script.md +129 -0
  178. package/docs/index.html +2262 -0
  179. package/docs/install-prompt.md +78 -0
  180. package/docs/install-windows.md +132 -0
  181. package/docs/install.ps1 +171 -0
  182. package/docs/install.sh +146 -0
  183. package/docs/launch/README.md +114 -0
  184. package/docs/launch/hn.md +49 -0
  185. package/docs/launch/reddit.md +45 -0
  186. package/docs/launch/x.md +65 -0
  187. package/docs/launch-post.md +112 -0
  188. package/docs/legal.md +78 -0
  189. package/docs/llm-providers.md +99 -0
  190. package/docs/multi-host-mcp.md +217 -0
  191. package/docs/multi-turn-architecture.md +108 -0
  192. package/docs/operations/deployment.md +108 -0
  193. package/docs/operations/error-monitoring.md +70 -0
  194. package/docs/operations/weekly-digest.md +70 -0
  195. package/docs/operations.md +494 -0
  196. package/docs/platform-support.md +133 -0
  197. package/docs/policy-packs.md +188 -0
  198. package/docs/press-kit/fact-sheet.md +78 -0
  199. package/docs/press-kit/founder-bio.md +3 -0
  200. package/docs/press-kit/logo.svg +33 -0
  201. package/docs/press-kit/screenshots.md +146 -0
  202. package/docs/pricing.md +251 -0
  203. package/docs/pro-backend-architecture.md +523 -0
  204. package/docs/producthunt.md +52 -0
  205. package/docs/router-migration.md +110 -0
  206. package/docs/session-log-schema.md +68 -0
  207. package/docs/social.md +95 -0
  208. package/docs/team-genome.md +528 -0
  209. package/docs/telemetry.md +194 -0
  210. package/docs/test-isolation.md +100 -0
  211. package/docs/upgrade.md +159 -0
  212. package/hooks/_hook-errors.ts +87 -0
  213. package/hooks/_hook-perf-emit.ts +113 -0
  214. package/hooks/_hook-timing-ledger.ts +1133 -0
  215. package/hooks/_nudge-throttle.ts +127 -0
  216. package/hooks/_recent-blocks.ts +127 -0
  217. package/hooks/audit-upload.ts +86 -0
  218. package/hooks/codex-hooks.json +171 -0
  219. package/hooks/commit-attribution.ts +179 -0
  220. package/hooks/edit-batching-nudge.ts +154 -0
  221. package/hooks/genome-scribe-hook.ts +219 -0
  222. package/hooks/hooks.json +294 -0
  223. package/hooks/orient-nudge-hook.ts +242 -0
  224. package/hooks/policy-enforce.ts +214 -0
  225. package/hooks/post-tool-use-embedding.ts +93 -0
  226. package/hooks/post-tool-use-genome.sh +37 -0
  227. package/hooks/post-tool-use-genome.ts +69 -0
  228. package/hooks/posttooluse-correlate.ts +122 -0
  229. package/hooks/posttooluse-dedupe-output.ts +241 -0
  230. package/hooks/posttooluse-genome-refresh.ts +223 -0
  231. package/hooks/posttooluse-native-nudge.ts +205 -0
  232. package/hooks/posttooluse-stale-result.ts +277 -0
  233. package/hooks/pretooluse-bash.ts +63 -0
  234. package/hooks/pretooluse-budget-guard.ts +318 -0
  235. package/hooks/pretooluse-common.ts +985 -0
  236. package/hooks/pretooluse-eco-router.ts +196 -0
  237. package/hooks/pretooluse-edit.sh +93 -0
  238. package/hooks/pretooluse-edit.ts +197 -0
  239. package/hooks/pretooluse-glob.ts +70 -0
  240. package/hooks/pretooluse-grep.sh +85 -0
  241. package/hooks/pretooluse-grep.ts +96 -0
  242. package/hooks/pretooluse-notebookedit.ts +121 -0
  243. package/hooks/pretooluse-read.sh +118 -0
  244. package/hooks/pretooluse-read.ts +118 -0
  245. package/hooks/pretooluse-task.ts +130 -0
  246. package/hooks/pretooluse-webfetch.ts +66 -0
  247. package/hooks/pretooluse-websearch.ts +85 -0
  248. package/hooks/pretooluse-write.ts +128 -0
  249. package/hooks/pulse-emit.ts +197 -0
  250. package/hooks/session-end-consolidate.sh +35 -0
  251. package/hooks/session-end-consolidate.ts +188 -0
  252. package/hooks/session-end-stats-gc.ts +80 -0
  253. package/hooks/session-log-append.sh +94 -0
  254. package/hooks/session-log-append.ts +116 -0
  255. package/hooks/session-start.ts +993 -0
  256. package/hooks/sessionend-hook-health-nudge.ts +451 -0
  257. package/hooks/sessionend-savings-nudge.ts +318 -0
  258. package/hooks/sessionend-status-line-nudge.ts +296 -0
  259. package/hooks/sessionstart-brief.ts +140 -0
  260. package/hooks/sessionstart-cost-refactor.ts +95 -0
  261. package/hooks/sessionstart-efficient.ts +110 -0
  262. package/hooks/sessionstart-genome-author.ts +93 -0
  263. package/hooks/sessionstart-lean-tools.ts +98 -0
  264. package/hooks/sessionstart-search.ts +97 -0
  265. package/hooks/stop-accounting.ts +152 -0
  266. package/hooks/subagent-stop-rollup.ts +162 -0
  267. package/hooks/userpromptsubmit-brief-trigger.ts +156 -0
  268. package/package.json +56 -0
  269. package/ports/README.md +108 -0
  270. package/ports/cursor/mcp.json +11 -0
  271. package/ports/goose/recipe.yaml +26 -0
  272. package/scripts/ashlr-mcp.ts +40 -0
  273. package/scripts/auto-update.ts +177 -0
  274. package/scripts/baseline-scan.ts +831 -0
  275. package/scripts/bench-orchestrate.ts +377 -0
  276. package/scripts/benchmark-refs.ts +292 -0
  277. package/scripts/bootstrap.mjs +100 -0
  278. package/scripts/brief-eval.ts +234 -0
  279. package/scripts/bump-version.ts +82 -0
  280. package/scripts/bun-resolve.d.mts +11 -0
  281. package/scripts/bun-resolve.mjs +59 -0
  282. package/scripts/calibrate-grep.ts +467 -0
  283. package/scripts/capture-grep-workload.ts +260 -0
  284. package/scripts/check-tier.ts +61 -0
  285. package/scripts/check-version-sync.ts +97 -0
  286. package/scripts/cli-orchestrate-status.ts +386 -0
  287. package/scripts/cli-orchestrate.ts +303 -0
  288. package/scripts/cli.ts +478 -0
  289. package/scripts/cloud-smoke-test.ts +313 -0
  290. package/scripts/coach-report.ts +373 -0
  291. package/scripts/context-db-migrate.ts +137 -0
  292. package/scripts/context-status.ts +43 -0
  293. package/scripts/demo-run.ts +255 -0
  294. package/scripts/deploy-smoke.sh +140 -0
  295. package/scripts/doctor.ts +992 -0
  296. package/scripts/embed-file-worker.ts +104 -0
  297. package/scripts/embed-tune.ts +308 -0
  298. package/scripts/errors-report.ts +428 -0
  299. package/scripts/find-test-leak.ts +69 -0
  300. package/scripts/generate-badge.ts +357 -0
  301. package/scripts/genome-auto-consolidate.ts +533 -0
  302. package/scripts/genome-auto-propose.ts +578 -0
  303. package/scripts/genome-cloud-pull.ts +363 -0
  304. package/scripts/genome-cloud-push.ts +382 -0
  305. package/scripts/genome-cloud-sync.ts +587 -0
  306. package/scripts/genome-commit-watcher.ts +295 -0
  307. package/scripts/genome-init.ts +986 -0
  308. package/scripts/genome-key.ts +233 -0
  309. package/scripts/genome-keygen.ts +168 -0
  310. package/scripts/genome-link.ts +59 -0
  311. package/scripts/genome-refresh-worker.ts +364 -0
  312. package/scripts/genome-rewrap.ts +90 -0
  313. package/scripts/genome-synthesizer.ts +679 -0
  314. package/scripts/genome-team-init.ts +312 -0
  315. package/scripts/handoff-pack.ts +438 -0
  316. package/scripts/hook-bootstrap.mjs +75 -0
  317. package/scripts/hook-timing-reader.ts +371 -0
  318. package/scripts/hook-timings-report.ts +554 -0
  319. package/scripts/install-genome-hooks.ts +196 -0
  320. package/scripts/install-onnx-model.ts +154 -0
  321. package/scripts/install-permissions.ts +276 -0
  322. package/scripts/install-status-line.ts +150 -0
  323. package/scripts/mcp-entrypoint.sh +78 -0
  324. package/scripts/mcp-entrypoint.ts +130 -0
  325. package/scripts/mdx-angle-bracket-lint.ts +152 -0
  326. package/scripts/measure-cold-start.ts +126 -0
  327. package/scripts/migrate-stats-to-sqlite.ts +86 -0
  328. package/scripts/ollama-setup.ts +498 -0
  329. package/scripts/onboarding-wizard.ts +1691 -0
  330. package/scripts/orchestrate-emit-telemetry.ts +177 -0
  331. package/scripts/orchestrate-expand.ts +517 -0
  332. package/scripts/orchestrate-run.ts +744 -0
  333. package/scripts/print-legend.ts +50 -0
  334. package/scripts/product-metadata.ts +38 -0
  335. package/scripts/prune-old-memories.ts +208 -0
  336. package/scripts/publish.sh +88 -0
  337. package/scripts/read-calibration.ts +122 -0
  338. package/scripts/report-crash.ts +226 -0
  339. package/scripts/run-benchmark.ts +750 -0
  340. package/scripts/run-root-tests.ts +28 -0
  341. package/scripts/savings-dashboard.ts +1258 -0
  342. package/scripts/savings-report-extras.ts +447 -0
  343. package/scripts/savings-status-line.ts +873 -0
  344. package/scripts/session-event-emit.ts +266 -0
  345. package/scripts/session-greet.ts +686 -0
  346. package/scripts/session-log-report.ts +551 -0
  347. package/scripts/session-resume.ts +614 -0
  348. package/scripts/set-hook-mode.ts +83 -0
  349. package/scripts/smoke-cross-platform.ts +117 -0
  350. package/scripts/smoke-realtime.ts +257 -0
  351. package/scripts/smoke-tools.ts +21 -0
  352. package/scripts/stats-cloud-pull.ts +213 -0
  353. package/scripts/stats-cloud-sync.ts +261 -0
  354. package/scripts/telemetry-flush.ts +160 -0
  355. package/scripts/telemetry-status.ts +266 -0
  356. package/scripts/timing-budget.ts +35 -0
  357. package/scripts/ui-animation.ts +120 -0
  358. package/scripts/ui.ts +431 -0
  359. package/scripts/upgrade-flow.ts +775 -0
  360. package/scripts/weekly-digest.ts +394 -0
  361. package/servers/_accounting.ts +78 -0
  362. package/servers/_ask-router.ts +84 -0
  363. package/servers/_ast-chunker.ts +434 -0
  364. package/servers/_ast-helpers.ts +183 -0
  365. package/servers/_ast-languages.ts +153 -0
  366. package/servers/_ast-refactor/_shared.ts +221 -0
  367. package/servers/_ast-refactor/cross-file-rename.ts +560 -0
  368. package/servers/_ast-refactor/extract-function.ts +357 -0
  369. package/servers/_ast-refactor/file-local-rename.ts +172 -0
  370. package/servers/_ast-refactor.ts +41 -0
  371. package/servers/_ast-skeleton.ts +107 -0
  372. package/servers/_bash-summarizers-registry.ts +1096 -0
  373. package/servers/_crash-dump.ts +210 -0
  374. package/servers/_cwd-clamp.ts +342 -0
  375. package/servers/_edit-log.ts +28 -0
  376. package/servers/_edit-match.ts +236 -0
  377. package/servers/_edit-validate.ts +196 -0
  378. package/servers/_embed-calibration.ts +123 -0
  379. package/servers/_embedding-cache.ts +401 -0
  380. package/servers/_embedding-model.ts +329 -0
  381. package/servers/_events.ts +113 -0
  382. package/servers/_genome-cache.ts +154 -0
  383. package/servers/_genome-commits.ts +172 -0
  384. package/servers/_genome-crypto-v2.ts +242 -0
  385. package/servers/_genome-crypto.ts +259 -0
  386. package/servers/_genome-discoveries.ts +140 -0
  387. package/servers/_genome-embed-populator.ts +216 -0
  388. package/servers/_genome-freshness.ts +134 -0
  389. package/servers/_genome-issues.ts +171 -0
  390. package/servers/_genome-live.ts +263 -0
  391. package/servers/_genome-prs.ts +188 -0
  392. package/servers/_genome-search.ts +479 -0
  393. package/servers/_genome-sync.ts +297 -0
  394. package/servers/_history-tracker.ts +279 -0
  395. package/servers/_http-helpers.ts +151 -0
  396. package/servers/_identity-hash.ts +151 -0
  397. package/servers/_llm-providers/anthropic.ts +132 -0
  398. package/servers/_llm-providers/cloud.ts +195 -0
  399. package/servers/_llm-providers/index.ts +350 -0
  400. package/servers/_llm-providers/local.ts +127 -0
  401. package/servers/_llm-providers/onnx.ts +393 -0
  402. package/servers/_llm-providers/types.ts +27 -0
  403. package/servers/_manifest-v2.ts +447 -0
  404. package/servers/_mcp-host.ts +93 -0
  405. package/servers/_nudge-events.ts +451 -0
  406. package/servers/_orchestrate-executor.ts +316 -0
  407. package/servers/_prefetch.ts +435 -0
  408. package/servers/_pricing.ts +256 -0
  409. package/servers/_pro.ts +381 -0
  410. package/servers/_read-cache.ts +44 -0
  411. package/servers/_router-handlers.ts +46 -0
  412. package/servers/_router.ts +55 -0
  413. package/servers/_run-with-timeout.ts +173 -0
  414. package/servers/_savings-render.ts +214 -0
  415. package/servers/_stats-sqlite.ts +640 -0
  416. package/servers/_stats.ts +1183 -0
  417. package/servers/_status-line-cells.ts +469 -0
  418. package/servers/_streaks.ts +162 -0
  419. package/servers/_summarize.ts +208 -0
  420. package/servers/_task-graph-render.ts +217 -0
  421. package/servers/_task-graph.ts +545 -0
  422. package/servers/_telemetry.ts +719 -0
  423. package/servers/_test-parsers.ts +530 -0
  424. package/servers/_test-watch.ts +626 -0
  425. package/servers/_text-helpers.ts +37 -0
  426. package/servers/_token-measure.ts +160 -0
  427. package/servers/_tool-base.ts +248 -0
  428. package/servers/_weekly-digest.ts +391 -0
  429. package/servers/ask-server-handlers.ts +49 -0
  430. package/servers/ask-server.ts +174 -0
  431. package/servers/bash-server-handlers.ts +138 -0
  432. package/servers/bash-server.ts +1136 -0
  433. package/servers/bench.ts +186 -0
  434. package/servers/diff-semantic-server-handlers.ts +39 -0
  435. package/servers/diff-semantic-server.ts +566 -0
  436. package/servers/diff-server-handlers.ts +317 -0
  437. package/servers/diff-server.ts +20 -0
  438. package/servers/edit-server-handlers.ts +31 -0
  439. package/servers/edit-server.ts +209 -0
  440. package/servers/edit-structural-server-handlers.ts +322 -0
  441. package/servers/efficiency-server-handlers.ts +161 -0
  442. package/servers/efficiency-server.ts +1135 -0
  443. package/servers/flush-server-handlers.ts +21 -0
  444. package/servers/flush-server.ts +19 -0
  445. package/servers/genome-server-handlers.ts +118 -0
  446. package/servers/genome-server.ts +397 -0
  447. package/servers/github-server-handlers.ts +187 -0
  448. package/servers/github-server.ts +856 -0
  449. package/servers/glob-server-handlers.ts +233 -0
  450. package/servers/glob-server.ts +20 -0
  451. package/servers/grep-server-handlers.ts +61 -0
  452. package/servers/grep-server.ts +564 -0
  453. package/servers/http-server-handlers.ts +36 -0
  454. package/servers/http-server.ts +149 -0
  455. package/servers/logs-server-handlers.ts +42 -0
  456. package/servers/logs-server.ts +359 -0
  457. package/servers/ls-server-handlers.ts +166 -0
  458. package/servers/ls-server.ts +20 -0
  459. package/servers/multi-edit-server-handlers.ts +51 -0
  460. package/servers/multi-edit-server.ts +422 -0
  461. package/servers/notebook-edit-server-handlers.ts +34 -0
  462. package/servers/notebook-edit-server.ts +181 -0
  463. package/servers/orient-server-handlers.ts +52 -0
  464. package/servers/orient-server.ts +436 -0
  465. package/servers/pipe-server-handlers.ts +68 -0
  466. package/servers/pipe-server.ts +300 -0
  467. package/servers/read-server-handlers.ts +42 -0
  468. package/servers/read-server.ts +206 -0
  469. package/servers/rename-file-server-handlers.ts +67 -0
  470. package/servers/rename-file-server.ts +699 -0
  471. package/servers/savings-server-handlers.ts +27 -0
  472. package/servers/savings-server.ts +54 -0
  473. package/servers/search-replace-regex-server-handlers.ts +96 -0
  474. package/servers/search-replace-regex-server.ts +751 -0
  475. package/servers/sql-server-handlers.ts +58 -0
  476. package/servers/sql-server.ts +505 -0
  477. package/servers/task-server-handlers.ts +358 -0
  478. package/servers/task-server.ts +24 -0
  479. package/servers/test-server-handlers.ts +351 -0
  480. package/servers/test-server.ts +20 -0
  481. package/servers/tree-server-handlers.ts +503 -0
  482. package/servers/tree-server.ts +20 -0
  483. package/servers/webfetch-server-handlers.ts +166 -0
  484. package/servers/webfetch-server.ts +20 -0
  485. package/servers/websearch-server-handlers.ts +215 -0
  486. package/servers/websearch-server.ts +19 -0
  487. package/servers/write-server-handlers.ts +27 -0
  488. package/servers/write-server.ts +97 -0
  489. package/skills/ashlr-brief/SKILL.md +80 -0
  490. package/skills/ashlr-codex-doctor/SKILL.md +21 -0
  491. package/skills/ashlr-codex-efficient-tools/SKILL.md +17 -0
  492. package/skills/ashlr-codex-genome-refresh/SKILL.md +20 -0
  493. package/skills/ashlr-codex-parallel-explore/SKILL.md +20 -0
  494. package/skills/ashlr-codex-savings/SKILL.md +17 -0
  495. package/skills/ashlr-cost-refactor/SKILL.md +50 -0
  496. package/skills/ashlr-efficient/SKILL.md +64 -0
  497. package/skills/ashlr-genome-author/SKILL.md +43 -0
  498. package/skills/ashlr-lean-tools/SKILL.md +43 -0
  499. package/skills/ashlr-search/SKILL.md +46 -0
@@ -0,0 +1,1113 @@
1
+ //! Types for working with [`File`].
2
+ //!
3
+ //! [`File`]: File
4
+
5
+ use crate::fs::{asyncify, OpenOptions};
6
+ use crate::io::blocking::{Buf, DEFAULT_MAX_BUF_SIZE};
7
+ use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
8
+ use crate::sync::Mutex;
9
+
10
+ use std::cmp;
11
+ use std::fmt;
12
+ use std::fs::{Metadata, Permissions};
13
+ use std::future::Future;
14
+ use std::io::{self, Seek, SeekFrom};
15
+ use std::path::Path;
16
+ use std::pin::Pin;
17
+ use std::sync::Arc;
18
+ use std::task::{ready, Context, Poll};
19
+
20
+ #[cfg(test)]
21
+ use super::mocks::JoinHandle;
22
+ #[cfg(test)]
23
+ use super::mocks::MockFile as StdFile;
24
+ #[cfg(test)]
25
+ use super::mocks::{spawn_blocking, spawn_mandatory_blocking};
26
+ #[cfg(not(test))]
27
+ use crate::blocking::JoinHandle;
28
+ #[cfg(not(test))]
29
+ use crate::blocking::{spawn_blocking, spawn_mandatory_blocking};
30
+ #[cfg(not(test))]
31
+ use std::fs::File as StdFile;
32
+
33
+ cfg_io_uring! {
34
+ #[cfg(not(test))]
35
+ use crate::spawn;
36
+ }
37
+
38
+ /// A reference to an open file on the filesystem.
39
+ ///
40
+ /// This is a specialized version of [`std::fs::File`] for usage from the
41
+ /// Tokio runtime.
42
+ ///
43
+ /// An instance of a `File` can be read and/or written depending on what options
44
+ /// it was opened with. Files also implement [`AsyncSeek`] to alter the logical
45
+ /// cursor that the file contains internally.
46
+ ///
47
+ /// A file will not be closed immediately when it goes out of scope if there
48
+ /// are any IO operations that have not yet completed. To ensure that a file is
49
+ /// closed immediately when it is dropped, you should call [`flush`] before
50
+ /// dropping it. Note that this does not ensure that the file has been fully
51
+ /// written to disk; the operating system might keep the changes around in an
52
+ /// in-memory buffer. See the [`sync_all`] method for telling the OS to write
53
+ /// the data to disk.
54
+ ///
55
+ /// Reading and writing to a `File` is usually done using the convenience
56
+ /// methods found on the [`AsyncReadExt`] and [`AsyncWriteExt`] traits.
57
+ ///
58
+ /// [`AsyncSeek`]: trait@crate::io::AsyncSeek
59
+ /// [`flush`]: fn@crate::io::AsyncWriteExt::flush
60
+ /// [`sync_all`]: fn@crate::fs::File::sync_all
61
+ /// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
62
+ /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
63
+ ///
64
+ /// # Examples
65
+ ///
66
+ /// Create a new file and asynchronously write bytes to it:
67
+ ///
68
+ /// ```no_run
69
+ /// use tokio::fs::File;
70
+ /// use tokio::io::AsyncWriteExt; // for write_all()
71
+ ///
72
+ /// # async fn dox() -> std::io::Result<()> {
73
+ /// let mut file = File::create("foo.txt").await?;
74
+ /// file.write_all(b"hello, world!").await?;
75
+ /// # Ok(())
76
+ /// # }
77
+ /// ```
78
+ ///
79
+ /// Read the contents of a file into a buffer:
80
+ ///
81
+ /// ```no_run
82
+ /// use tokio::fs::File;
83
+ /// use tokio::io::AsyncReadExt; // for read_to_end()
84
+ ///
85
+ /// # async fn dox() -> std::io::Result<()> {
86
+ /// let mut file = File::open("foo.txt").await?;
87
+ ///
88
+ /// let mut contents = vec![];
89
+ /// file.read_to_end(&mut contents).await?;
90
+ ///
91
+ /// println!("len = {}", contents.len());
92
+ /// # Ok(())
93
+ /// # }
94
+ /// ```
95
+ pub struct File {
96
+ std: Arc<StdFile>,
97
+ inner: Mutex<Inner>,
98
+ max_buf_size: usize,
99
+ }
100
+
101
+ struct Inner {
102
+ state: State,
103
+
104
+ /// Errors from writes/flushes are returned in write/flush calls. If a write
105
+ /// error is observed while performing a read, it is saved until the next
106
+ /// write / flush call.
107
+ last_write_err: Option<io::ErrorKind>,
108
+
109
+ pos: u64,
110
+ }
111
+
112
+ #[derive(Debug)]
113
+ enum State {
114
+ Idle(Option<Buf>),
115
+ Busy(JoinHandle<(Operation, Buf)>),
116
+ }
117
+
118
+ #[derive(Debug)]
119
+ enum Operation {
120
+ Read(io::Result<usize>),
121
+ Write(io::Result<()>),
122
+ Seek(io::Result<u64>),
123
+ }
124
+
125
+ impl File {
126
+ /// Attempts to open a file in read-only mode.
127
+ ///
128
+ /// See [`OpenOptions`] for more details.
129
+ ///
130
+ /// # Errors
131
+ ///
132
+ /// This function will return an error if called from outside of the Tokio
133
+ /// runtime or if path does not already exist. Other errors may also be
134
+ /// returned according to `OpenOptions::open`.
135
+ ///
136
+ /// # Examples
137
+ ///
138
+ /// ```no_run
139
+ /// use tokio::fs::File;
140
+ /// use tokio::io::AsyncReadExt;
141
+ ///
142
+ /// # async fn dox() -> std::io::Result<()> {
143
+ /// let mut file = File::open("foo.txt").await?;
144
+ ///
145
+ /// let mut contents = vec![];
146
+ /// file.read_to_end(&mut contents).await?;
147
+ ///
148
+ /// println!("len = {}", contents.len());
149
+ /// # Ok(())
150
+ /// # }
151
+ /// ```
152
+ ///
153
+ /// The [`read_to_end`] method is defined on the [`AsyncReadExt`] trait.
154
+ ///
155
+ /// [`read_to_end`]: fn@crate::io::AsyncReadExt::read_to_end
156
+ /// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
157
+ pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
158
+ Self::options().read(true).open(path).await
159
+ }
160
+
161
+ /// Opens a file in write-only mode.
162
+ ///
163
+ /// This function will create a file if it does not exist, and will truncate
164
+ /// it if it does.
165
+ ///
166
+ /// See [`OpenOptions`] for more details.
167
+ ///
168
+ /// # Errors
169
+ ///
170
+ /// Results in an error if called from outside of the Tokio runtime or if
171
+ /// the underlying [`create`] call results in an error.
172
+ ///
173
+ /// [`create`]: std::fs::File::create
174
+ ///
175
+ /// # Examples
176
+ ///
177
+ /// ```no_run
178
+ /// use tokio::fs::File;
179
+ /// use tokio::io::AsyncWriteExt;
180
+ ///
181
+ /// # async fn dox() -> std::io::Result<()> {
182
+ /// let mut file = File::create("foo.txt").await?;
183
+ /// file.write_all(b"hello, world!").await?;
184
+ /// # Ok(())
185
+ /// # }
186
+ /// ```
187
+ ///
188
+ /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
189
+ ///
190
+ /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
191
+ /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
192
+ pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
193
+ Self::options()
194
+ .write(true)
195
+ .create(true)
196
+ .truncate(true)
197
+ .open(path)
198
+ .await
199
+ }
200
+
201
+ /// Opens a file in read-write mode.
202
+ ///
203
+ /// This function will create a file if it does not exist, or return an error
204
+ /// if it does. This way, if the call succeeds, the file returned is guaranteed
205
+ /// to be new.
206
+ ///
207
+ /// This option is useful because it is atomic. Otherwise between checking
208
+ /// whether a file exists and creating a new one, the file may have been
209
+ /// created by another process (a TOCTOU race condition / attack).
210
+ ///
211
+ /// This can also be written using `File::options().read(true).write(true).create_new(true).open(...)`.
212
+ ///
213
+ /// See [`OpenOptions`] for more details.
214
+ ///
215
+ /// # Examples
216
+ ///
217
+ /// ```no_run
218
+ /// use tokio::fs::File;
219
+ /// use tokio::io::AsyncWriteExt;
220
+ ///
221
+ /// # async fn dox() -> std::io::Result<()> {
222
+ /// let mut file = File::create_new("foo.txt").await?;
223
+ /// file.write_all(b"hello, world!").await?;
224
+ /// # Ok(())
225
+ /// # }
226
+ /// ```
227
+ ///
228
+ /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
229
+ ///
230
+ /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
231
+ /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
232
+ pub async fn create_new<P: AsRef<Path>>(path: P) -> std::io::Result<File> {
233
+ Self::options()
234
+ .read(true)
235
+ .write(true)
236
+ .create_new(true)
237
+ .open(path)
238
+ .await
239
+ }
240
+
241
+ /// Returns a new [`OpenOptions`] object.
242
+ ///
243
+ /// This function returns a new `OpenOptions` object that you can use to
244
+ /// open or create a file with specific options if `open()` or `create()`
245
+ /// are not appropriate.
246
+ ///
247
+ /// It is equivalent to `OpenOptions::new()`, but allows you to write more
248
+ /// readable code. Instead of
249
+ /// `OpenOptions::new().append(true).open("example.log")`,
250
+ /// you can write `File::options().append(true).open("example.log")`. This
251
+ /// also avoids the need to import `OpenOptions`.
252
+ ///
253
+ /// See the [`OpenOptions::new`] function for more details.
254
+ ///
255
+ /// # Examples
256
+ ///
257
+ /// ```no_run
258
+ /// use tokio::fs::File;
259
+ /// use tokio::io::AsyncWriteExt;
260
+ ///
261
+ /// # async fn dox() -> std::io::Result<()> {
262
+ /// let mut f = File::options().append(true).open("example.log").await?;
263
+ /// f.write_all(b"new line\n").await?;
264
+ /// # Ok(())
265
+ /// # }
266
+ /// ```
267
+ #[must_use]
268
+ pub fn options() -> OpenOptions {
269
+ OpenOptions::new()
270
+ }
271
+
272
+ /// Converts a [`std::fs::File`] to a [`tokio::fs::File`](File).
273
+ ///
274
+ /// # Examples
275
+ ///
276
+ /// ```no_run
277
+ /// // This line could block. It is not recommended to do this on the Tokio
278
+ /// // runtime.
279
+ /// let std_file = std::fs::File::open("foo.txt").unwrap();
280
+ /// let file = tokio::fs::File::from_std(std_file);
281
+ /// ```
282
+ pub fn from_std(std: StdFile) -> File {
283
+ File {
284
+ std: Arc::new(std),
285
+ inner: Mutex::new(Inner {
286
+ state: State::Idle(Some(Buf::with_capacity(0))),
287
+ last_write_err: None,
288
+ pos: 0,
289
+ }),
290
+ max_buf_size: DEFAULT_MAX_BUF_SIZE,
291
+ }
292
+ }
293
+
294
+ /// Attempts to sync all OS-internal metadata to disk.
295
+ ///
296
+ /// This function will attempt to ensure that all in-core data reaches the
297
+ /// filesystem before returning.
298
+ ///
299
+ /// # Examples
300
+ ///
301
+ /// ```no_run
302
+ /// use tokio::fs::File;
303
+ /// use tokio::io::AsyncWriteExt;
304
+ ///
305
+ /// # async fn dox() -> std::io::Result<()> {
306
+ /// let mut file = File::create("foo.txt").await?;
307
+ /// file.write_all(b"hello, world!").await?;
308
+ /// file.sync_all().await?;
309
+ /// # Ok(())
310
+ /// # }
311
+ /// ```
312
+ ///
313
+ /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
314
+ ///
315
+ /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
316
+ /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
317
+ pub async fn sync_all(&self) -> io::Result<()> {
318
+ let mut inner = self.inner.lock().await;
319
+ inner.complete_inflight().await;
320
+
321
+ let std = self.std.clone();
322
+ asyncify(move || std.sync_all()).await
323
+ }
324
+
325
+ /// This function is similar to `sync_all`, except that it may not
326
+ /// synchronize file metadata to the filesystem.
327
+ ///
328
+ /// This is intended for use cases that must synchronize content, but don't
329
+ /// need the metadata on disk. The goal of this method is to reduce disk
330
+ /// operations.
331
+ ///
332
+ /// Note that some platforms may simply implement this in terms of `sync_all`.
333
+ ///
334
+ /// # Examples
335
+ ///
336
+ /// ```no_run
337
+ /// use tokio::fs::File;
338
+ /// use tokio::io::AsyncWriteExt;
339
+ ///
340
+ /// # async fn dox() -> std::io::Result<()> {
341
+ /// let mut file = File::create("foo.txt").await?;
342
+ /// file.write_all(b"hello, world!").await?;
343
+ /// file.sync_data().await?;
344
+ /// # Ok(())
345
+ /// # }
346
+ /// ```
347
+ ///
348
+ /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
349
+ ///
350
+ /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
351
+ /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
352
+ pub async fn sync_data(&self) -> io::Result<()> {
353
+ let mut inner = self.inner.lock().await;
354
+ inner.complete_inflight().await;
355
+
356
+ let std = self.std.clone();
357
+ asyncify(move || std.sync_data()).await
358
+ }
359
+
360
+ /// Truncates or extends the underlying file, updating the size of this file to become size.
361
+ ///
362
+ /// If the size is less than the current file's size, then the file will be
363
+ /// shrunk. If it is greater than the current file's size, then the file
364
+ /// will be extended to size and have all of the intermediate data filled in
365
+ /// with 0s.
366
+ ///
367
+ /// # Errors
368
+ ///
369
+ /// This function will return an error if the file is not opened for
370
+ /// writing.
371
+ ///
372
+ /// # Examples
373
+ ///
374
+ /// ```no_run
375
+ /// use tokio::fs::File;
376
+ /// use tokio::io::AsyncWriteExt;
377
+ ///
378
+ /// # async fn dox() -> std::io::Result<()> {
379
+ /// let mut file = File::create("foo.txt").await?;
380
+ /// file.write_all(b"hello, world!").await?;
381
+ /// file.set_len(10).await?;
382
+ /// # Ok(())
383
+ /// # }
384
+ /// ```
385
+ ///
386
+ /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
387
+ ///
388
+ /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
389
+ /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
390
+ pub async fn set_len(&self, size: u64) -> io::Result<()> {
391
+ let mut inner = self.inner.lock().await;
392
+ inner.complete_inflight().await;
393
+
394
+ let mut buf = match inner.state {
395
+ State::Idle(ref mut buf_cell) => buf_cell.take().unwrap(),
396
+ _ => unreachable!(),
397
+ };
398
+
399
+ let seek = if !buf.is_empty() {
400
+ Some(SeekFrom::Current(buf.discard_read()))
401
+ } else {
402
+ None
403
+ };
404
+
405
+ let std = self.std.clone();
406
+
407
+ inner.state = State::Busy(spawn_blocking(move || {
408
+ let res = if let Some(seek) = seek {
409
+ (&*std).seek(seek).and_then(|_| std.set_len(size))
410
+ } else {
411
+ std.set_len(size)
412
+ }
413
+ .map(|()| 0); // the value is discarded later
414
+
415
+ // Return the result as a seek
416
+ (Operation::Seek(res), buf)
417
+ }));
418
+
419
+ let (op, buf) = match inner.state {
420
+ State::Idle(_) => unreachable!(),
421
+ State::Busy(ref mut rx) => rx.await?,
422
+ };
423
+
424
+ inner.state = State::Idle(Some(buf));
425
+
426
+ match op {
427
+ Operation::Seek(res) => res.map(|pos| {
428
+ inner.pos = pos;
429
+ }),
430
+ _ => unreachable!(),
431
+ }
432
+ }
433
+
434
+ /// Queries metadata about the underlying file.
435
+ ///
436
+ /// # Examples
437
+ ///
438
+ /// ```no_run
439
+ /// use tokio::fs::File;
440
+ ///
441
+ /// # async fn dox() -> std::io::Result<()> {
442
+ /// let file = File::open("foo.txt").await?;
443
+ /// let metadata = file.metadata().await?;
444
+ ///
445
+ /// println!("{:?}", metadata);
446
+ /// # Ok(())
447
+ /// # }
448
+ /// ```
449
+ pub async fn metadata(&self) -> io::Result<Metadata> {
450
+ let std = self.std.clone();
451
+ asyncify(move || std.metadata()).await
452
+ }
453
+
454
+ /// Creates a new `File` instance that shares the same underlying file handle
455
+ /// as the existing `File` instance. Reads, writes, and seeks will affect both
456
+ /// File instances simultaneously.
457
+ ///
458
+ /// # Examples
459
+ ///
460
+ /// ```no_run
461
+ /// use tokio::fs::File;
462
+ ///
463
+ /// # async fn dox() -> std::io::Result<()> {
464
+ /// let file = File::open("foo.txt").await?;
465
+ /// let file_clone = file.try_clone().await?;
466
+ /// # Ok(())
467
+ /// # }
468
+ /// ```
469
+ pub async fn try_clone(&self) -> io::Result<File> {
470
+ self.inner.lock().await.complete_inflight().await;
471
+ let std = self.std.clone();
472
+ let std_file = asyncify(move || std.try_clone()).await?;
473
+ let mut file = File::from_std(std_file);
474
+ file.set_max_buf_size(self.max_buf_size);
475
+ Ok(file)
476
+ }
477
+
478
+ /// Destructures `File` into a [`std::fs::File`]. This function is
479
+ /// async to allow any in-flight operations to complete.
480
+ ///
481
+ /// Use `File::try_into_std` to attempt conversion immediately.
482
+ ///
483
+ /// # Examples
484
+ ///
485
+ /// ```no_run
486
+ /// use tokio::fs::File;
487
+ ///
488
+ /// # async fn dox() -> std::io::Result<()> {
489
+ /// let tokio_file = File::open("foo.txt").await?;
490
+ /// let std_file = tokio_file.into_std().await;
491
+ /// # Ok(())
492
+ /// # }
493
+ /// ```
494
+ pub async fn into_std(mut self) -> StdFile {
495
+ self.inner.get_mut().complete_inflight().await;
496
+ Arc::try_unwrap(self.std).expect("Arc::try_unwrap failed")
497
+ }
498
+
499
+ /// Tries to immediately destructure `File` into a [`std::fs::File`].
500
+ ///
501
+ /// # Errors
502
+ ///
503
+ /// This function will return an error containing the file if some
504
+ /// operation is in-flight.
505
+ ///
506
+ /// # Examples
507
+ ///
508
+ /// ```no_run
509
+ /// use tokio::fs::File;
510
+ ///
511
+ /// # async fn dox() -> std::io::Result<()> {
512
+ /// let tokio_file = File::open("foo.txt").await?;
513
+ /// let std_file = tokio_file.try_into_std().unwrap();
514
+ /// # Ok(())
515
+ /// # }
516
+ /// ```
517
+ #[allow(clippy::result_large_err)]
518
+ pub fn try_into_std(mut self) -> Result<StdFile, Self> {
519
+ match Arc::try_unwrap(self.std) {
520
+ Ok(file) => Ok(file),
521
+ Err(std_file_arc) => {
522
+ self.std = std_file_arc;
523
+ Err(self)
524
+ }
525
+ }
526
+ }
527
+
528
+ /// Changes the permissions on the underlying file.
529
+ ///
530
+ /// # Platform-specific behavior
531
+ ///
532
+ /// This function currently corresponds to the `fchmod` function on Unix and
533
+ /// the `SetFileInformationByHandle` function on Windows. Note that, this
534
+ /// [may change in the future][changes].
535
+ ///
536
+ /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
537
+ ///
538
+ /// # Errors
539
+ ///
540
+ /// This function will return an error if the user lacks permission change
541
+ /// attributes on the underlying file. It may also return an error in other
542
+ /// os-specific unspecified cases.
543
+ ///
544
+ /// # Examples
545
+ ///
546
+ /// ```no_run
547
+ /// use tokio::fs::File;
548
+ ///
549
+ /// # async fn dox() -> std::io::Result<()> {
550
+ /// let file = File::open("foo.txt").await?;
551
+ /// let mut perms = file.metadata().await?.permissions();
552
+ /// perms.set_readonly(true);
553
+ /// file.set_permissions(perms).await?;
554
+ /// # Ok(())
555
+ /// # }
556
+ /// ```
557
+ pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
558
+ let std = self.std.clone();
559
+ asyncify(move || std.set_permissions(perm)).await
560
+ }
561
+
562
+ /// Set the maximum buffer size for the underlying [`AsyncRead`] / [`AsyncWrite`] operation.
563
+ ///
564
+ /// Although Tokio uses a sensible default value for this buffer size, this function would be
565
+ /// useful for changing that default depending on the situation.
566
+ ///
567
+ /// # Examples
568
+ ///
569
+ /// ```no_run
570
+ /// use tokio::fs::File;
571
+ /// use tokio::io::AsyncWriteExt;
572
+ ///
573
+ /// # async fn dox() -> std::io::Result<()> {
574
+ /// let mut file = File::open("foo.txt").await?;
575
+ ///
576
+ /// // Set maximum buffer size to 8 MiB
577
+ /// file.set_max_buf_size(8 * 1024 * 1024);
578
+ ///
579
+ /// let mut buf = vec![1; 1024 * 1024 * 1024];
580
+ ///
581
+ /// // Write the 1 GiB buffer in chunks up to 8 MiB each.
582
+ /// file.write_all(&mut buf).await?;
583
+ /// # Ok(())
584
+ /// # }
585
+ /// ```
586
+ pub fn set_max_buf_size(&mut self, max_buf_size: usize) {
587
+ self.max_buf_size = max_buf_size;
588
+ }
589
+
590
+ /// Get the maximum buffer size for the underlying [`AsyncRead`] / [`AsyncWrite`] operation.
591
+ pub fn max_buf_size(&self) -> usize {
592
+ self.max_buf_size
593
+ }
594
+ }
595
+
596
+ impl AsyncRead for File {
597
+ fn poll_read(
598
+ self: Pin<&mut Self>,
599
+ cx: &mut Context<'_>,
600
+ dst: &mut ReadBuf<'_>,
601
+ ) -> Poll<io::Result<()>> {
602
+ ready!(crate::trace::trace_leaf(cx));
603
+
604
+ let me = self.get_mut();
605
+ let inner = me.inner.get_mut();
606
+
607
+ loop {
608
+ match inner.state {
609
+ State::Idle(ref mut buf_cell) => {
610
+ let mut buf = buf_cell.take().unwrap();
611
+
612
+ if !buf.is_empty() || dst.remaining() == 0 {
613
+ buf.copy_to(dst);
614
+ *buf_cell = Some(buf);
615
+ return Poll::Ready(Ok(()));
616
+ }
617
+
618
+ let std = me.std.clone();
619
+
620
+ let max_buf_size = cmp::min(dst.remaining(), me.max_buf_size);
621
+ inner.state = State::Busy(Inner::poll_read_inner(std, buf, max_buf_size)?);
622
+ }
623
+ State::Busy(ref mut rx) => {
624
+ let (op, mut buf) = ready!(Pin::new(rx).poll(cx))?;
625
+
626
+ match op {
627
+ Operation::Read(Ok(_)) => {
628
+ buf.copy_to(dst);
629
+ inner.state = State::Idle(Some(buf));
630
+ return Poll::Ready(Ok(()));
631
+ }
632
+ Operation::Read(Err(e)) => {
633
+ assert!(buf.is_empty());
634
+
635
+ inner.state = State::Idle(Some(buf));
636
+ return Poll::Ready(Err(e));
637
+ }
638
+ Operation::Write(Ok(())) => {
639
+ assert!(buf.is_empty());
640
+ inner.state = State::Idle(Some(buf));
641
+ continue;
642
+ }
643
+ Operation::Write(Err(e)) => {
644
+ assert!(inner.last_write_err.is_none());
645
+ inner.last_write_err = Some(e.kind());
646
+ inner.state = State::Idle(Some(buf));
647
+ }
648
+ Operation::Seek(result) => {
649
+ assert!(buf.is_empty());
650
+ inner.state = State::Idle(Some(buf));
651
+ if let Ok(pos) = result {
652
+ inner.pos = pos;
653
+ }
654
+ continue;
655
+ }
656
+ }
657
+ }
658
+ }
659
+ }
660
+ }
661
+ }
662
+
663
+ impl AsyncSeek for File {
664
+ fn start_seek(self: Pin<&mut Self>, mut pos: SeekFrom) -> io::Result<()> {
665
+ let me = self.get_mut();
666
+ let inner = me.inner.get_mut();
667
+
668
+ match inner.state {
669
+ State::Busy(_) => Err(io::Error::new(
670
+ io::ErrorKind::Other,
671
+ "other file operation is pending, call poll_complete before start_seek",
672
+ )),
673
+ State::Idle(ref mut buf_cell) => {
674
+ let mut buf = buf_cell.take().unwrap();
675
+
676
+ // Factor in any unread data from the buf
677
+ if !buf.is_empty() {
678
+ let n = buf.discard_read();
679
+
680
+ if let SeekFrom::Current(ref mut offset) = pos {
681
+ *offset += n;
682
+ }
683
+ }
684
+
685
+ let std = me.std.clone();
686
+
687
+ inner.state = State::Busy(spawn_blocking(move || {
688
+ let res = (&*std).seek(pos);
689
+ (Operation::Seek(res), buf)
690
+ }));
691
+ Ok(())
692
+ }
693
+ }
694
+ }
695
+
696
+ fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
697
+ ready!(crate::trace::trace_leaf(cx));
698
+ let inner = self.inner.get_mut();
699
+
700
+ loop {
701
+ match inner.state {
702
+ State::Idle(_) => return Poll::Ready(Ok(inner.pos)),
703
+ State::Busy(ref mut rx) => {
704
+ let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
705
+ inner.state = State::Idle(Some(buf));
706
+
707
+ match op {
708
+ Operation::Read(_) => {}
709
+ Operation::Write(Err(e)) => {
710
+ assert!(inner.last_write_err.is_none());
711
+ inner.last_write_err = Some(e.kind());
712
+ }
713
+ Operation::Write(_) => {}
714
+ Operation::Seek(res) => {
715
+ if let Ok(pos) = res {
716
+ inner.pos = pos;
717
+ }
718
+ return Poll::Ready(res);
719
+ }
720
+ }
721
+ }
722
+ }
723
+ }
724
+ }
725
+ }
726
+
727
+ impl AsyncWrite for File {
728
+ fn poll_write(
729
+ self: Pin<&mut Self>,
730
+ cx: &mut Context<'_>,
731
+ src: &[u8],
732
+ ) -> Poll<io::Result<usize>> {
733
+ ready!(crate::trace::trace_leaf(cx));
734
+ let me = self.get_mut();
735
+ let inner = me.inner.get_mut();
736
+
737
+ if let Some(e) = inner.last_write_err.take() {
738
+ return Poll::Ready(Err(e.into()));
739
+ }
740
+
741
+ loop {
742
+ match inner.state {
743
+ State::Idle(ref mut buf_cell) => {
744
+ let mut buf = buf_cell.take().unwrap();
745
+
746
+ let seek = if !buf.is_empty() {
747
+ Some(SeekFrom::Current(buf.discard_read()))
748
+ } else {
749
+ None
750
+ };
751
+
752
+ let n = buf.copy_from(src, me.max_buf_size);
753
+ let std = me.std.clone();
754
+
755
+ let blocking_task_join_handle = spawn_mandatory_blocking(move || {
756
+ let res = if let Some(seek) = seek {
757
+ (&*std).seek(seek).and_then(|_| buf.write_to(&mut &*std))
758
+ } else {
759
+ buf.write_to(&mut &*std)
760
+ };
761
+
762
+ (Operation::Write(res), buf)
763
+ })
764
+ .ok_or_else(|| {
765
+ io::Error::new(io::ErrorKind::Other, "background task failed")
766
+ })?;
767
+
768
+ inner.state = State::Busy(blocking_task_join_handle);
769
+
770
+ return Poll::Ready(Ok(n));
771
+ }
772
+ State::Busy(ref mut rx) => {
773
+ let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
774
+ inner.state = State::Idle(Some(buf));
775
+
776
+ match op {
777
+ Operation::Read(_) => {
778
+ // We don't care about the result here. The fact
779
+ // that the cursor has advanced will be reflected in
780
+ // the next iteration of the loop
781
+ continue;
782
+ }
783
+ Operation::Write(res) => {
784
+ // If the previous write was successful, continue.
785
+ // Otherwise, error.
786
+ res?;
787
+ continue;
788
+ }
789
+ Operation::Seek(_) => {
790
+ // Ignore the seek
791
+ continue;
792
+ }
793
+ }
794
+ }
795
+ }
796
+ }
797
+ }
798
+
799
+ fn poll_write_vectored(
800
+ self: Pin<&mut Self>,
801
+ cx: &mut Context<'_>,
802
+ bufs: &[io::IoSlice<'_>],
803
+ ) -> Poll<Result<usize, io::Error>> {
804
+ ready!(crate::trace::trace_leaf(cx));
805
+ let me = self.get_mut();
806
+ let inner = me.inner.get_mut();
807
+
808
+ if let Some(e) = inner.last_write_err.take() {
809
+ return Poll::Ready(Err(e.into()));
810
+ }
811
+
812
+ loop {
813
+ match inner.state {
814
+ State::Idle(ref mut buf_cell) => {
815
+ let mut buf = buf_cell.take().unwrap();
816
+
817
+ let seek = if !buf.is_empty() {
818
+ Some(SeekFrom::Current(buf.discard_read()))
819
+ } else {
820
+ None
821
+ };
822
+
823
+ let n = buf.copy_from_bufs(bufs, me.max_buf_size);
824
+ let std = me.std.clone();
825
+
826
+ let blocking_task_join_handle = spawn_mandatory_blocking(move || {
827
+ let res = if let Some(seek) = seek {
828
+ (&*std).seek(seek).and_then(|_| buf.write_to(&mut &*std))
829
+ } else {
830
+ buf.write_to(&mut &*std)
831
+ };
832
+
833
+ (Operation::Write(res), buf)
834
+ })
835
+ .ok_or_else(|| {
836
+ io::Error::new(io::ErrorKind::Other, "background task failed")
837
+ })?;
838
+
839
+ inner.state = State::Busy(blocking_task_join_handle);
840
+
841
+ return Poll::Ready(Ok(n));
842
+ }
843
+ State::Busy(ref mut rx) => {
844
+ let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
845
+ inner.state = State::Idle(Some(buf));
846
+
847
+ match op {
848
+ Operation::Read(_) => {
849
+ // We don't care about the result here. The fact
850
+ // that the cursor has advanced will be reflected in
851
+ // the next iteration of the loop
852
+ continue;
853
+ }
854
+ Operation::Write(res) => {
855
+ // If the previous write was successful, continue.
856
+ // Otherwise, error.
857
+ res?;
858
+ continue;
859
+ }
860
+ Operation::Seek(_) => {
861
+ // Ignore the seek
862
+ continue;
863
+ }
864
+ }
865
+ }
866
+ }
867
+ }
868
+ }
869
+
870
+ fn is_write_vectored(&self) -> bool {
871
+ true
872
+ }
873
+
874
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
875
+ ready!(crate::trace::trace_leaf(cx));
876
+ let inner = self.inner.get_mut();
877
+ inner.poll_flush(cx)
878
+ }
879
+
880
+ fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
881
+ ready!(crate::trace::trace_leaf(cx));
882
+ self.poll_flush(cx)
883
+ }
884
+ }
885
+
886
+ impl From<StdFile> for File {
887
+ fn from(std: StdFile) -> Self {
888
+ Self::from_std(std)
889
+ }
890
+ }
891
+
892
+ impl fmt::Debug for File {
893
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
894
+ fmt.debug_struct("tokio::fs::File")
895
+ .field("std", &self.std)
896
+ .finish()
897
+ }
898
+ }
899
+
900
+ #[cfg(unix)]
901
+ impl std::os::unix::io::AsRawFd for File {
902
+ fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
903
+ self.std.as_raw_fd()
904
+ }
905
+ }
906
+
907
+ #[cfg(unix)]
908
+ impl std::os::unix::io::AsFd for File {
909
+ fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
910
+ unsafe {
911
+ std::os::unix::io::BorrowedFd::borrow_raw(std::os::unix::io::AsRawFd::as_raw_fd(self))
912
+ }
913
+ }
914
+ }
915
+
916
+ #[cfg(unix)]
917
+ impl std::os::unix::io::FromRawFd for File {
918
+ unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
919
+ // Safety: exactly the same safety contract as
920
+ // `std::os::unix::io::FromRawFd::from_raw_fd`.
921
+ unsafe { StdFile::from_raw_fd(fd).into() }
922
+ }
923
+ }
924
+
925
+ cfg_windows! {
926
+ use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle};
927
+
928
+ impl AsRawHandle for File {
929
+ fn as_raw_handle(&self) -> RawHandle {
930
+ self.std.as_raw_handle()
931
+ }
932
+ }
933
+
934
+ impl AsHandle for File {
935
+ fn as_handle(&self) -> BorrowedHandle<'_> {
936
+ unsafe {
937
+ BorrowedHandle::borrow_raw(
938
+ AsRawHandle::as_raw_handle(self),
939
+ )
940
+ }
941
+ }
942
+ }
943
+
944
+ impl FromRawHandle for File {
945
+ unsafe fn from_raw_handle(handle: RawHandle) -> Self {
946
+ // Safety: exactly the same safety contract as
947
+ // `FromRawHandle::from_raw_handle`.
948
+ unsafe { StdFile::from_raw_handle(handle).into() }
949
+ }
950
+ }
951
+ }
952
+
953
+ impl Inner {
954
+ fn poll_read_inner(
955
+ std: Arc<StdFile>,
956
+ buf: Buf,
957
+ max_buf_size: usize,
958
+ ) -> io::Result<JoinHandle<(Operation, Buf)>> {
959
+ // Unit tests use `MockFile` and the mock `spawn_blocking` infrastructure,
960
+ // which can't drive real io_uring operations. The io_uring read path
961
+ // is tested through integration tests in `tests/fs_uring_file_read.rs`.
962
+ #[cfg(all(
963
+ not(test),
964
+ tokio_unstable,
965
+ feature = "io-uring",
966
+ feature = "rt",
967
+ feature = "fs",
968
+ target_os = "linux",
969
+ ))]
970
+ {
971
+ if let Ok(handle) = crate::runtime::Handle::try_current() {
972
+ let driver_handle = handle.inner.driver().io();
973
+
974
+ if driver_handle.is_uring_ready(io_uring::opcode::Read::CODE) {
975
+ // Fast path: uring already initialized and Read supported.
976
+ let fd: crate::io::uring::utils::ArcFd = std;
977
+ return Ok(spawn(Self::uring_read(fd, buf, max_buf_size)));
978
+ }
979
+
980
+ if !driver_handle.is_uring_probed() {
981
+ // Not yet probed: lazy init inside an async task so
982
+ // `File::from_std()` can still benefit from io-uring.
983
+ return Ok(spawn(Self::lazy_init_read(std, buf, max_buf_size)));
984
+ }
985
+ // Probed but unsupported: fall through to spawn_blocking.
986
+ }
987
+ }
988
+
989
+ // Fallback: spawn_blocking
990
+ let join = Self::spawn_blocking_read(buf, std, max_buf_size);
991
+ Ok(join)
992
+ }
993
+
994
+ /// Perform an io-uring read with interrupt retry.
995
+ #[cfg(all(
996
+ not(test),
997
+ tokio_unstable,
998
+ feature = "io-uring",
999
+ feature = "rt",
1000
+ feature = "fs",
1001
+ target_os = "linux",
1002
+ ))]
1003
+ async fn uring_read(
1004
+ mut fd: crate::io::uring::utils::ArcFd,
1005
+ mut buf: Buf,
1006
+ max_buf_size: usize,
1007
+ ) -> (Operation, Buf) {
1008
+ use crate::runtime::driver::op::Op;
1009
+
1010
+ loop {
1011
+ let (res, r_fd, r_buf) =
1012
+ // u64::MAX to use and advance the file position
1013
+ Op::read_at(fd, buf, max_buf_size, u64::MAX).await;
1014
+ match res {
1015
+ Err(e) if e.kind() == io::ErrorKind::Interrupted => {
1016
+ buf = r_buf;
1017
+ fd = r_fd;
1018
+ continue;
1019
+ }
1020
+ Err(e) => break (Operation::Read(Err(e)), r_buf),
1021
+ Ok(n) => break (Operation::Read(Ok(n as usize)), r_buf),
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ /// Attempt lazy io-uring initialization, then read via uring or fall back
1027
+ /// to a blocking read. Covers the `File::from_std()` path where
1028
+ /// `check_and_init()` hasn't been called yet.
1029
+ #[cfg(all(
1030
+ not(test),
1031
+ tokio_unstable,
1032
+ feature = "io-uring",
1033
+ feature = "rt",
1034
+ feature = "fs",
1035
+ target_os = "linux",
1036
+ ))]
1037
+ async fn lazy_init_read(std: Arc<StdFile>, buf: Buf, max_buf_size: usize) -> (Operation, Buf) {
1038
+ let handle = crate::runtime::Handle::current();
1039
+ let driver_handle = handle.inner.driver().io();
1040
+ if driver_handle
1041
+ .check_and_init(io_uring::opcode::Read::CODE)
1042
+ .await
1043
+ .unwrap_or(false)
1044
+ {
1045
+ let fd: crate::io::uring::utils::ArcFd = std;
1046
+ Self::uring_read(fd, buf, max_buf_size).await
1047
+ } else {
1048
+ match Self::spawn_blocking_read(buf, std, max_buf_size).await {
1049
+ Ok(result) => result,
1050
+ Err(e) => (
1051
+ Operation::Read(Err(io::Error::new(io::ErrorKind::Other, e))),
1052
+ Buf::with_capacity(0),
1053
+ ),
1054
+ }
1055
+ }
1056
+ }
1057
+
1058
+ fn spawn_blocking_read(
1059
+ buf: Buf,
1060
+ std: Arc<StdFile>,
1061
+ max_buf_size: usize,
1062
+ ) -> JoinHandle<(Operation, Buf)> {
1063
+ spawn_blocking(move || {
1064
+ let mut buf = buf;
1065
+ // SAFETY: the `Read` implementation of `std` does not
1066
+ // read from the buffer it is borrowing and correctly
1067
+ // reports the length of the data written into the buffer.
1068
+ let res = unsafe { buf.read_from(&mut &*std, max_buf_size) };
1069
+ (Operation::Read(res), buf)
1070
+ })
1071
+ }
1072
+
1073
+ async fn complete_inflight(&mut self) {
1074
+ use std::future::poll_fn;
1075
+
1076
+ poll_fn(|cx| self.poll_complete_inflight(cx)).await;
1077
+ }
1078
+
1079
+ fn poll_complete_inflight(&mut self, cx: &mut Context<'_>) -> Poll<()> {
1080
+ ready!(crate::trace::trace_leaf(cx));
1081
+ match self.poll_flush(cx) {
1082
+ Poll::Ready(Err(e)) => {
1083
+ self.last_write_err = Some(e.kind());
1084
+ Poll::Ready(())
1085
+ }
1086
+ Poll::Ready(Ok(())) => Poll::Ready(()),
1087
+ Poll::Pending => Poll::Pending,
1088
+ }
1089
+ }
1090
+
1091
+ fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
1092
+ if let Some(e) = self.last_write_err.take() {
1093
+ return Poll::Ready(Err(e.into()));
1094
+ }
1095
+
1096
+ let (op, buf) = match self.state {
1097
+ State::Idle(_) => return Poll::Ready(Ok(())),
1098
+ State::Busy(ref mut rx) => ready!(Pin::new(rx).poll(cx))?,
1099
+ };
1100
+
1101
+ // The buffer is not used here
1102
+ self.state = State::Idle(Some(buf));
1103
+
1104
+ match op {
1105
+ Operation::Read(_) => Poll::Ready(Ok(())),
1106
+ Operation::Write(res) => Poll::Ready(res),
1107
+ Operation::Seek(_) => Poll::Ready(Ok(())),
1108
+ }
1109
+ }
1110
+ }
1111
+
1112
+ #[cfg(test)]
1113
+ mod tests;