superacli 1.1.6 → 1.1.8
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/CONTRIBUTING.md +270 -0
- package/README.md +141 -54
- package/__tests__/adapter-schema.test.js +251 -86
- package/__tests__/azd-plugin.test.js +109 -0
- package/__tests__/config.test.js +4 -3
- package/__tests__/discover.test.js +59 -0
- package/__tests__/goose-plugin.test.js +149 -0
- package/__tests__/help-json.test.js +2 -0
- package/__tests__/openhands-plugin.test.js +106 -0
- package/__tests__/plugin-cocoindex-code-uninstall.test.js +19 -0
- package/__tests__/plugin-cocoindex-code.test.js +37 -0
- package/__tests__/plugin-install-guidance.test.js +81 -0
- package/__tests__/plugins-registry.test.js +44 -0
- package/__tests__/plugins-store.test.js +40 -5
- package/__tests__/process-adapter.test.js +50 -1
- package/__tests__/resend-plugin.test.js +109 -82
- package/__tests__/server-app.test.js +1 -0
- package/__tests__/server-routes-commands.test.js +20 -2
- package/__tests__/server-routes-plugins.test.js +130 -0
- package/__tests__/skills.test.js +26 -0
- package/__tests__/squirrelscan-plugin.test.js +129 -0
- package/__tests__/uipath-plugin.test.js +104 -0
- package/__tests__/uipathcli-plugin.test.js +95 -0
- package/cli/adapters/mcp.js +2 -0
- package/cli/adapters/process.js +50 -7
- package/cli/config.js +240 -3
- package/cli/discover.js +157 -0
- package/cli/help-json.js +16 -1
- package/cli/plugin-install-guidance.js +92 -37
- package/cli/plugins-manager.js +4 -3
- package/cli/plugins-registry.js +74 -8
- package/cli/plugins-store.js +78 -17
- package/cli/skills-mcp.js +1 -1
- package/cli/skills.js +39 -2
- package/cli/supercli.js +255 -63
- package/docs/feature-gaps.md +8 -8
- package/docs/features/azd-uipath-plugins.md +43 -0
- package/docs/features/server-plugins.md +62 -0
- package/docs/features/skills.md +9 -5
- package/docs/index.html +183 -123
- package/docs/{supported-harnesses.md → plugins-available.md} +4 -3
- package/docs/{plugin-harness-guide.md → plugins-how-to.md} +1 -1
- package/docs/plugins.md +26 -20
- package/docs/server-plugins-usage-guide.md +182 -0
- package/docs/skills-catalog.md +12 -10
- package/index.html +384 -0
- package/package.json +2 -2
- package/plugins/agent-browser/README.md +69 -0
- package/plugins/agent-browser/plugin.json +111 -0
- package/plugins/agent-browser/skills/quickstart/SKILL.md +66 -0
- package/plugins/aider/README.md +53 -0
- package/plugins/aider/plugin.json +105 -0
- package/plugins/aider/scripts/aider-wrapper.js +243 -0
- package/plugins/aider/scripts/setup-aider.js +37 -0
- package/plugins/aider/skills/dry-run-review.md +24 -0
- package/plugins/aider/skills/model-and-provider.md +24 -0
- package/plugins/aider/skills/one-shot-edits.md +30 -0
- package/plugins/aider/skills/quickstart/SKILL.md +51 -0
- package/plugins/azd/README.md +28 -0
- package/plugins/azd/plugin.json +87 -0
- package/plugins/azd/skills/quickstart/SKILL.md +41 -0
- package/plugins/blogwatcher/README.md +3 -3
- package/plugins/boxlite/Dockerfile +9 -0
- package/plugins/boxlite/README.md +62 -0
- package/plugins/boxlite/plugin.json +201 -0
- package/plugins/boxlite/scripts/run-boxlite.js +106 -0
- package/plugins/boxlite/skills/quickstart/SKILL.md +40 -0
- package/plugins/cass/plugin.json +150 -0
- package/plugins/cass/scripts/setup-cass.js +47 -0
- package/plugins/cass/skills/quickstart/SKILL.md +46 -0
- package/plugins/clever/README.md +46 -0
- package/plugins/clever/plugin.json +119 -0
- package/plugins/clever/scripts/setup-clever.js +28 -0
- package/plugins/clever/skills/auth-and-profile.md +29 -0
- package/plugins/clever/skills/passthrough-safety.md +21 -0
- package/plugins/clever/skills/quickstart/SKILL.md +45 -0
- package/plugins/clever/skills/resource-inventory.md +24 -0
- package/plugins/clix/README.md +4 -4
- package/plugins/cocoindex-code/README.md +64 -0
- package/plugins/cocoindex-code/plugin.json +81 -0
- package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-310.pyc +0 -0
- package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-311.pyc +0 -0
- package/plugins/cocoindex-code/scripts/post-install.js +61 -0
- package/plugins/cocoindex-code/scripts/post-uninstall.js +25 -0
- package/plugins/cocoindex-code/scripts/query.py +88 -0
- package/plugins/cocoindex-code/scripts/run-query.js +50 -0
- package/plugins/cocoindex-code/skills/quickstart/SKILL.md +73 -0
- package/plugins/copilot/README.md +24 -0
- package/plugins/copilot/plugin.json +80 -0
- package/plugins/copilot/skills/quickstart/SKILL.md +44 -0
- package/plugins/gemini/README.md +24 -0
- package/plugins/gemini/plugin.json +98 -0
- package/plugins/gemini/skills/quickstart/SKILL.md +44 -0
- package/plugins/gifcap/plugin.json +119 -0
- package/plugins/gifcap/scripts/setup-gifcap.js +44 -0
- package/plugins/gifcap/skills/quickstart/SKILL.md +34 -0
- package/plugins/gifcap/test-record-quiet.gif +0 -0
- package/plugins/gifcap/test-record.gif +0 -0
- package/plugins/goose/README.md +36 -0
- package/plugins/goose/plugin.json +183 -0
- package/plugins/goose/skills/quickstart/SKILL.md +44 -0
- package/plugins/json-server/README.md +58 -0
- package/plugins/json-server/plugin.json +113 -0
- package/plugins/json-server/skills/quickstart/SKILL.md +57 -0
- package/plugins/lightpanda/README.md +145 -0
- package/plugins/lightpanda/package-lock.json +1375 -0
- package/plugins/lightpanda/package.json +12 -0
- package/plugins/lightpanda/plugin.json +116 -0
- package/plugins/lightpanda/scripts/lightpanda-contacts.js +494 -0
- package/plugins/lightpanda/scripts/lightpanda-generic-extract.js +403 -0
- package/plugins/lightpanda/scripts/lightpanda-wrapper.js +480 -0
- package/plugins/lightpanda/scripts/setup-lightpanda.js +39 -0
- package/plugins/lightpanda/skills/contact-discovery.md +51 -0
- package/plugins/lightpanda/skills/generic-extraction.md +66 -0
- package/plugins/lightpanda/skills/quickstart/SKILL.md +103 -0
- package/plugins/lightpanda/skills/resilient-navigation.md +42 -0
- package/plugins/monty/README.md +2 -2
- package/plugins/nullclaw/README.md +3 -3
- package/plugins/offline-ai/README.md +23 -0
- package/plugins/offline-ai/plugin.json +82 -0
- package/plugins/offline-ai/skills/quickstart/SKILL.md +43 -0
- package/plugins/openhands/README.md +25 -0
- package/plugins/openhands/plugin.json +116 -0
- package/plugins/openhands/skills/quickstart/SKILL.md +26 -0
- package/plugins/plandex/README.md +25 -0
- package/plugins/plandex/plugin.json +130 -0
- package/plugins/plandex/skills/quickstart/SKILL.md +50 -0
- package/plugins/plugins.json +199 -0
- package/plugins/squirrelscan/Dockerfile +5 -0
- package/plugins/squirrelscan/README.md +47 -0
- package/plugins/squirrelscan/plugin.json +493 -0
- package/plugins/squirrelscan/scripts/post-install.js +33 -0
- package/plugins/squirrelscan/scripts/post-uninstall.js +25 -0
- package/plugins/squirrelscan/scripts/run-squirrel.js +73 -0
- package/plugins/squirrelscan/skills/audit-workflow/SKILL.md +33 -0
- package/plugins/squirrelscan/skills/publish-report/SKILL.md +33 -0
- package/plugins/squirrelscan/skills/quickstart/SKILL.md +41 -0
- package/plugins/uipath/README.md +27 -0
- package/plugins/uipath/plugin.json +86 -0
- package/plugins/uipath/skills/quickstart/SKILL.md +47 -0
- package/plugins/uipathcli/README.md +28 -0
- package/plugins/uipathcli/plugin.json +120 -0
- package/plugins/uipathcli/scripts/run-uipath-cli.js +49 -0
- package/plugins/uipathcli/skills/quickstart/SKILL.md +22 -0
- package/plugins/xurl/README.md +4 -4
- package/server/app.js +5 -2
- package/server/public/app.js +3 -0
- package/server/routes/commands.js +95 -12
- package/server/routes/plugins.js +262 -0
- package/server/services/pluginsService.js +303 -0
- package/server/views/command-edit.ejs +196 -14
- package/server/views/partials/head.ejs +1 -0
- package/server/views/plugins.ejs +264 -0
- package/tests/test-plugins-registry.js +30 -0
- package/.beads/.br_history/issues.20260308_200823_636718328.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_200823_636718328.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200827_033159453.jsonl +0 -21
- package/.beads/.br_history/issues.20260308_200827_033159453.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200829_595900053.jsonl +0 -22
- package/.beads/.br_history/issues.20260308_200829_595900053.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200834_079930100.jsonl +0 -23
- package/.beads/.br_history/issues.20260308_200834_079930100.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200858_370924996.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_200858_370924996.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201031_019730855.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201031_019730855.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201031_578974884.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201031_578974884.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201054_780345548.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201054_780345548.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201054_896980019.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201054_896980019.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201128_599819688.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201128_599819688.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201128_710221699.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201128_710221699.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201204_745649213.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201204_745649213.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201338_908436144.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201338_908436144.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201344_734860714.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_201344_734860714.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201630_819282295.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_201630_819282295.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203538_054279699.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_203538_054279699.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_597113070.jsonl +0 -26
- package/.beads/.br_history/issues.20260308_203547_597113070.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_775139216.jsonl +0 -27
- package/.beads/.br_history/issues.20260308_203547_775139216.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_950724773.jsonl +0 -28
- package/.beads/.br_history/issues.20260308_203547_950724773.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203548_107684523.jsonl +0 -29
- package/.beads/.br_history/issues.20260308_203548_107684523.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203548_310389993.jsonl +0 -30
- package/.beads/.br_history/issues.20260308_203548_310389993.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203825_953337320.jsonl +0 -31
- package/.beads/.br_history/issues.20260308_203825_953337320.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_204056_071377736.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_204056_071377736.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_517616844.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_517616844.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_648994024.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_648994024.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_867598036.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_867598036.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_094157355.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_094157355.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_327315677.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_327315677.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_545563822.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_545563822.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_061989333.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_061989333.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_181103364.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_181103364.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_408872234.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_408872234.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_616681652.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_616681652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_821507069.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_821507069.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205214_026661112.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205214_026661112.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205454_955250554.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205454_955250554.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205556_337800392.jsonl +0 -33
- package/.beads/.br_history/issues.20260308_205556_337800392.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205824_274686694.jsonl +0 -33
- package/.beads/.br_history/issues.20260308_205824_274686694.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_210240_583768328.jsonl +0 -34
- package/.beads/.br_history/issues.20260308_210240_583768328.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212223_641541494.jsonl +0 -34
- package/.beads/.br_history/issues.20260308_212223_641541494.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212227_735550996.jsonl +0 -35
- package/.beads/.br_history/issues.20260308_212227_735550996.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212232_547298548.jsonl +0 -36
- package/.beads/.br_history/issues.20260308_212232_547298548.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212528_843628125.jsonl +0 -37
- package/.beads/.br_history/issues.20260308_212528_843628125.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_094530502.jsonl +0 -38
- package/.beads/.br_history/issues.20260308_212529_094530502.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_331000853.jsonl +0 -39
- package/.beads/.br_history/issues.20260308_212529_331000853.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_587925652.jsonl +0 -40
- package/.beads/.br_history/issues.20260308_212529_587925652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212804_927764103.jsonl +0 -41
- package/.beads/.br_history/issues.20260308_212804_927764103.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_153673453.jsonl +0 -42
- package/.beads/.br_history/issues.20260308_212805_153673453.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_415982363.jsonl +0 -43
- package/.beads/.br_history/issues.20260308_212805_415982363.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_657497741.jsonl +0 -44
- package/.beads/.br_history/issues.20260308_212805_657497741.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_952838724.jsonl +0 -45
- package/.beads/.br_history/issues.20260308_212805_952838724.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_325433779.jsonl +0 -46
- package/.beads/.br_history/issues.20260308_212806_325433779.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_584685598.jsonl +0 -47
- package/.beads/.br_history/issues.20260308_212806_584685598.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_827817208.jsonl +0 -48
- package/.beads/.br_history/issues.20260308_212806_827817208.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_111320451.jsonl +0 -49
- package/.beads/.br_history/issues.20260308_212807_111320451.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_409545536.jsonl +0 -50
- package/.beads/.br_history/issues.20260308_212807_409545536.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_625063294.jsonl +0 -51
- package/.beads/.br_history/issues.20260308_212807_625063294.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_843906551.jsonl +0 -52
- package/.beads/.br_history/issues.20260308_212807_843906551.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_100304073.jsonl +0 -53
- package/.beads/.br_history/issues.20260308_212808_100304073.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_324723976.jsonl +0 -54
- package/.beads/.br_history/issues.20260308_212808_324723976.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_557513104.jsonl +0 -55
- package/.beads/.br_history/issues.20260308_212808_557513104.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_788048322.jsonl +0 -56
- package/.beads/.br_history/issues.20260308_212808_788048322.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213702_613249728.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213702_613249728.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213715_115792063.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213715_115792063.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213715_462220666.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213715_462220666.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213727_191258923.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213727_191258923.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213727_684383652.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213727_684383652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213735_751882991.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213735_751882991.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222052_279844960.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222052_279844960.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222056_873282114.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222056_873282114.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222103_402410761.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222103_402410761.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +0 -58
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +0 -59
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl.meta.json +0 -1
- package/.beads/config.yaml +0 -4
- package/.beads/issues.jsonl +0 -60
- package/.beads/metadata.json +0 -4
- package/docs/docs.html +0 -224
- package/docs/mcp-cheatsheet.md +0 -324
- package/docs/visual-overview.md +0 -21
- package/ref-monty/.cargo/config.toml +0 -3
- package/ref-monty/.claude/settings.json +0 -60
- package/ref-monty/.claude/skills/fastmod/SKILL.md +0 -22
- package/ref-monty/.claude/skills/python-playground/SKILL.md +0 -47
- package/ref-monty/.codecov.yml +0 -12
- package/ref-monty/.github/actions/build-pgo-wheel/action.yml +0 -72
- package/ref-monty/.github/workflows/ci.yml +0 -776
- package/ref-monty/.github/workflows/codspeed.yml +0 -45
- package/ref-monty/.github/workflows/init-npm-packages.yml +0 -82
- package/ref-monty/.pre-commit-config.yaml +0 -47
- package/ref-monty/.python-version +0 -1
- package/ref-monty/.rustfmt.toml +0 -4
- package/ref-monty/.zed/settings.json +0 -11
- package/ref-monty/CLAUDE.md +0 -535
- package/ref-monty/Cargo.lock +0 -3798
- package/ref-monty/Cargo.toml +0 -87
- package/ref-monty/LICENSE +0 -21
- package/ref-monty/Makefile +0 -216
- package/ref-monty/README.md +0 -430
- package/ref-monty/RELEASING.md +0 -47
- package/ref-monty/crates/fuzz/Cargo.toml +0 -30
- package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +0 -37
- package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +0 -552
- package/ref-monty/crates/monty/Cargo.toml +0 -68
- package/ref-monty/crates/monty/benches/main.rs +0 -247
- package/ref-monty/crates/monty/build.rs +0 -10
- package/ref-monty/crates/monty/src/args.rs +0 -733
- package/ref-monty/crates/monty/src/asyncio.rs +0 -179
- package/ref-monty/crates/monty/src/builtins/abs.rs +0 -55
- package/ref-monty/crates/monty/src/builtins/all.rs +0 -30
- package/ref-monty/crates/monty/src/builtins/any.rs +0 -30
- package/ref-monty/crates/monty/src/builtins/bin.rs +0 -59
- package/ref-monty/crates/monty/src/builtins/chr.rs +0 -46
- package/ref-monty/crates/monty/src/builtins/divmod.rs +0 -164
- package/ref-monty/crates/monty/src/builtins/enumerate.rs +0 -52
- package/ref-monty/crates/monty/src/builtins/filter.rs +0 -67
- package/ref-monty/crates/monty/src/builtins/getattr.rs +0 -65
- package/ref-monty/crates/monty/src/builtins/hash.rs +0 -28
- package/ref-monty/crates/monty/src/builtins/hex.rs +0 -58
- package/ref-monty/crates/monty/src/builtins/id.rs +0 -24
- package/ref-monty/crates/monty/src/builtins/isinstance.rs +0 -68
- package/ref-monty/crates/monty/src/builtins/len.rs +0 -25
- package/ref-monty/crates/monty/src/builtins/map.rs +0 -98
- package/ref-monty/crates/monty/src/builtins/min_max.rs +0 -113
- package/ref-monty/crates/monty/src/builtins/mod.rs +0 -246
- package/ref-monty/crates/monty/src/builtins/next.rs +0 -21
- package/ref-monty/crates/monty/src/builtins/oct.rs +0 -59
- package/ref-monty/crates/monty/src/builtins/ord.rs +0 -67
- package/ref-monty/crates/monty/src/builtins/pow.rs +0 -365
- package/ref-monty/crates/monty/src/builtins/print.rs +0 -141
- package/ref-monty/crates/monty/src/builtins/repr.rs +0 -16
- package/ref-monty/crates/monty/src/builtins/reversed.rs +0 -28
- package/ref-monty/crates/monty/src/builtins/round.rs +0 -174
- package/ref-monty/crates/monty/src/builtins/sorted.rs +0 -151
- package/ref-monty/crates/monty/src/builtins/sum.rs +0 -66
- package/ref-monty/crates/monty/src/builtins/type_.rs +0 -16
- package/ref-monty/crates/monty/src/builtins/zip.rs +0 -77
- package/ref-monty/crates/monty/src/bytecode/builder.rs +0 -699
- package/ref-monty/crates/monty/src/bytecode/code.rs +0 -310
- package/ref-monty/crates/monty/src/bytecode/compiler.rs +0 -3206
- package/ref-monty/crates/monty/src/bytecode/mod.rs +0 -24
- package/ref-monty/crates/monty/src/bytecode/op.rs +0 -617
- package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +0 -1058
- package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +0 -63
- package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +0 -487
- package/ref-monty/crates/monty/src/bytecode/vm/call.rs +0 -767
- package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +0 -741
- package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +0 -147
- package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +0 -297
- package/ref-monty/crates/monty/src/bytecode/vm/format.rs +0 -132
- package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +0 -1958
- package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +0 -620
- package/ref-monty/crates/monty/src/exception_private.rs +0 -1513
- package/ref-monty/crates/monty/src/exception_public.rs +0 -346
- package/ref-monty/crates/monty/src/expressions.rs +0 -694
- package/ref-monty/crates/monty/src/fstring.rs +0 -854
- package/ref-monty/crates/monty/src/function.rs +0 -119
- package/ref-monty/crates/monty/src/heap.rs +0 -1073
- package/ref-monty/crates/monty/src/heap_data.rs +0 -985
- package/ref-monty/crates/monty/src/heap_traits.rs +0 -312
- package/ref-monty/crates/monty/src/intern.rs +0 -837
- package/ref-monty/crates/monty/src/io.rs +0 -106
- package/ref-monty/crates/monty/src/lib.rs +0 -52
- package/ref-monty/crates/monty/src/modules/asyncio.rs +0 -144
- package/ref-monty/crates/monty/src/modules/math.rs +0 -1453
- package/ref-monty/crates/monty/src/modules/mod.rs +0 -120
- package/ref-monty/crates/monty/src/modules/os.rs +0 -116
- package/ref-monty/crates/monty/src/modules/pathlib.rs +0 -33
- package/ref-monty/crates/monty/src/modules/re.rs +0 -606
- package/ref-monty/crates/monty/src/modules/sys.rs +0 -60
- package/ref-monty/crates/monty/src/modules/typing.rs +0 -70
- package/ref-monty/crates/monty/src/namespace.rs +0 -21
- package/ref-monty/crates/monty/src/object.rs +0 -1040
- package/ref-monty/crates/monty/src/os.rs +0 -215
- package/ref-monty/crates/monty/src/parse.rs +0 -1730
- package/ref-monty/crates/monty/src/prepare.rs +0 -3015
- package/ref-monty/crates/monty/src/repl.rs +0 -1109
- package/ref-monty/crates/monty/src/resource.rs +0 -559
- package/ref-monty/crates/monty/src/run.rs +0 -457
- package/ref-monty/crates/monty/src/run_progress.rs +0 -821
- package/ref-monty/crates/monty/src/signature.rs +0 -651
- package/ref-monty/crates/monty/src/sorting.rs +0 -100
- package/ref-monty/crates/monty/src/types/bytes.rs +0 -2356
- package/ref-monty/crates/monty/src/types/dataclass.rs +0 -345
- package/ref-monty/crates/monty/src/types/dict.rs +0 -879
- package/ref-monty/crates/monty/src/types/dict_view.rs +0 -619
- package/ref-monty/crates/monty/src/types/iter.rs +0 -799
- package/ref-monty/crates/monty/src/types/list.rs +0 -929
- package/ref-monty/crates/monty/src/types/long_int.rs +0 -211
- package/ref-monty/crates/monty/src/types/mod.rs +0 -48
- package/ref-monty/crates/monty/src/types/module.rs +0 -146
- package/ref-monty/crates/monty/src/types/namedtuple.rs +0 -261
- package/ref-monty/crates/monty/src/types/path.rs +0 -596
- package/ref-monty/crates/monty/src/types/property.rs +0 -35
- package/ref-monty/crates/monty/src/types/py_trait.rs +0 -322
- package/ref-monty/crates/monty/src/types/range.rs +0 -285
- package/ref-monty/crates/monty/src/types/re_match.rs +0 -522
- package/ref-monty/crates/monty/src/types/re_pattern.rs +0 -726
- package/ref-monty/crates/monty/src/types/set.rs +0 -1373
- package/ref-monty/crates/monty/src/types/slice.rs +0 -257
- package/ref-monty/crates/monty/src/types/str.rs +0 -2051
- package/ref-monty/crates/monty/src/types/tuple.rs +0 -376
- package/ref-monty/crates/monty/src/types/type.rs +0 -407
- package/ref-monty/crates/monty/src/value.rs +0 -2558
- package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__id_too_many.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_too_many.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +0 -9
- package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +0 -9
- package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fail.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +0 -3
- package/ref-monty/crates/monty/test_cases/assert__ops.py +0 -11
- package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +0 -47
- package/ref-monty/crates/monty/test_cases/async__basic.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__closure.py +0 -14
- package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +0 -16
- package/ref-monty/crates/monty/test_cases/async__exception.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__ext_call.py +0 -73
- package/ref-monty/crates/monty/test_cases/async__gather_all.py +0 -85
- package/ref-monty/crates/monty/test_cases/async__nested_await.py +0 -15
- package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +0 -37
- package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__not_imported.py +0 -14
- package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +0 -27
- package/ref-monty/crates/monty/test_cases/async__return_types.py +0 -31
- package/ref-monty/crates/monty/test_cases/async__sequential.py +0 -16
- package/ref-monty/crates/monty/test_cases/async__traceback.py +0 -19
- package/ref-monty/crates/monty/test_cases/async__with_args.py +0 -14
- package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +0 -12
- package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +0 -68
- package/ref-monty/crates/monty/test_cases/bool__ops.py +0 -20
- package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/builtin__filter.py +0 -62
- package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +0 -11
- package/ref-monty/crates/monty/test_cases/builtin__getattr.py +0 -84
- package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +0 -42
- package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +0 -66
- package/ref-monty/crates/monty/test_cases/builtin__map.py +0 -74
- package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +0 -11
- package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +0 -154
- package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +0 -148
- package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +0 -10
- package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +0 -9
- package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +0 -12
- package/ref-monty/crates/monty/test_cases/builtin__repr.py +0 -3
- package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +0 -73
- package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +0 -18
- package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__methods.py +0 -394
- package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +0 -9
- package/ref-monty/crates/monty/test_cases/bytes__ops.py +0 -90
- package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/call_object.py +0 -3
- package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +0 -79
- package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +0 -81
- package/ref-monty/crates/monty/test_cases/closure__pep448.py +0 -203
- package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +0 -13
- package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +0 -120
- package/ref-monty/crates/monty/test_cases/comprehension__all.py +0 -208
- package/ref-monty/crates/monty/test_cases/comprehension__scope.py +0 -7
- package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +0 -14
- package/ref-monty/crates/monty/test_cases/dataclass__basic.py +0 -238
- package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__methods.py +0 -151
- package/ref-monty/crates/monty/test_cases/dict__ops.py +0 -133
- package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +0 -4
- package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__views.py +0 -165
- package/ref-monty/crates/monty/test_cases/edge__all.py +0 -26
- package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +0 -2
- package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +0 -2
- package/ref-monty/crates/monty/test_cases/exc__args.py +0 -16
- package/ref-monty/crates/monty/test_cases/exc__str.py +0 -15
- package/ref-monty/crates/monty/test_cases/execute_ok__all.py +0 -54
- package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +0 -3
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +0 -22
- package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +0 -34
- package/ref-monty/crates/monty/test_cases/ext_call__basic.py +0 -99
- package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +0 -37
- package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +0 -16
- package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +0 -26
- package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +0 -18
- package/ref-monty/crates/monty/test_cases/ext_call__elif.py +0 -171
- package/ref-monty/crates/monty/test_cases/ext_call__exc.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +0 -39
- package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +0 -31
- package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +0 -171
- package/ref-monty/crates/monty/test_cases/ext_call__for.py +0 -114
- package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +0 -12
- package/ref-monty/crates/monty/test_cases/ext_call__if.py +0 -135
- package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +0 -37
- package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +0 -14
- package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +0 -40
- package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__literals.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +0 -32
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +0 -69
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +0 -14
- package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +0 -19
- package/ref-monty/crates/monty/test_cases/ext_call__return.py +0 -28
- package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +0 -25
- package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +0 -28
- package/ref-monty/crates/monty/test_cases/ext_call__try.py +0 -280
- package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +0 -10
- package/ref-monty/crates/monty/test_cases/ext_call__unary.py +0 -13
- package/ref-monty/crates/monty/test_cases/frozenset__ops.py +0 -178
- package/ref-monty/crates/monty/test_cases/fstring__all.py +0 -236
- package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +0 -3
- package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__call_unpack.py +0 -42
- package/ref-monty/crates/monty/test_cases/function__defaults.py +0 -117
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +0 -13
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +0 -8
- package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +0 -173
- package/ref-monty/crates/monty/test_cases/function__ops.py +0 -294
- package/ref-monty/crates/monty/test_cases/function__return_none.py +0 -42
- package/ref-monty/crates/monty/test_cases/function__signatures.py +0 -47
- package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +0 -6
- package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +0 -7
- package/ref-monty/crates/monty/test_cases/global__ops.py +0 -163
- package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +0 -2
- package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +0 -2
- package/ref-monty/crates/monty/test_cases/hash__ops.py +0 -153
- package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +0 -3
- package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +0 -5
- package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +0 -3
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +0 -10
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +0 -6
- package/ref-monty/crates/monty/test_cases/id__ops.py +0 -97
- package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +0 -3
- package/ref-monty/crates/monty/test_cases/if__elif_else.py +0 -207
- package/ref-monty/crates/monty/test_cases/if__raise_elif.py +0 -11
- package/ref-monty/crates/monty/test_cases/if__raise_else.py +0 -13
- package/ref-monty/crates/monty/test_cases/if__raise_if.py +0 -9
- package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +0 -18
- package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +0 -16
- package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +0 -55
- package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__local_scope.py +0 -68
- package/ref-monty/crates/monty/test_cases/import__os.py +0 -25
- package/ref-monty/crates/monty/test_cases/import__relative_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +0 -14
- package/ref-monty/crates/monty/test_cases/import__star_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/import__sys.py +0 -47
- package/ref-monty/crates/monty/test_cases/import__sys_monty.py +0 -28
- package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +0 -37
- package/ref-monty/crates/monty/test_cases/import__typing.py +0 -25
- package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +0 -4
- package/ref-monty/crates/monty/test_cases/int__bigint.py +0 -467
- package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +0 -260
- package/ref-monty/crates/monty/test_cases/int__ops.py +0 -219
- package/ref-monty/crates/monty/test_cases/int__overflow_division.py +0 -84
- package/ref-monty/crates/monty/test_cases/is_variant__all.py +0 -36
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +0 -4
- package/ref-monty/crates/monty/test_cases/iter__for.py +0 -243
- package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +0 -66
- package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +0 -20
- package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +0 -7
- package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +0 -3
- package/ref-monty/crates/monty/test_cases/lambda__all.py +0 -145
- package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +0 -7
- package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +0 -3
- package/ref-monty/crates/monty/test_cases/list__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__ops.py +0 -473
- package/ref-monty/crates/monty/test_cases/list__pop_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +0 -13
- package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +0 -13
- package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/longint__index_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/loop__break_continue.py +0 -113
- package/ref-monty/crates/monty/test_cases/loop__break_finally.py +0 -69
- package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +0 -13
- package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +0 -55
- package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +0 -81
- package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +0 -13
- package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +0 -60
- package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__module.py +0 -1432
- package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +0 -259
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +0 -19
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +0 -12
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +0 -16
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +0 -10
- package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +0 -11
- package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +0 -34
- package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +0 -3
- package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +0 -353
- package/ref-monty/crates/monty/test_cases/os__environ.py +0 -40
- package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +0 -5
- package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +0 -5
- package/ref-monty/crates/monty/test_cases/parse_error__complex.py +0 -3
- package/ref-monty/crates/monty/test_cases/pathlib__import.py +0 -11
- package/ref-monty/crates/monty/test_cases/pathlib__os.py +0 -136
- package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/pathlib__pure.py +0 -81
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +0 -5
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +0 -6
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +0 -5
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +0 -6
- package/ref-monty/crates/monty/test_cases/range__error_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/range__ops.py +0 -236
- package/ref-monty/crates/monty/test_cases/re__basic.py +0 -756
- package/ref-monty/crates/monty/test_cases/re__grouping.py +0 -241
- package/ref-monty/crates/monty/test_cases/re__match.py +0 -148
- package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +0 -26
- package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +0 -23
- package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +0 -46
- package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +0 -12
- package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +0 -13
- package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +0 -18
- package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +0 -12
- package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +0 -14
- package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +0 -6
- package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +0 -16
- package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +0 -18
- package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +0 -25
- package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +0 -27
- package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +0 -6
- package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +0 -37
- package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +0 -34
- package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +0 -31
- package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__single_list.py +0 -3
- package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +0 -24
- package/ref-monty/crates/monty/test_cases/set__ops.py +0 -191
- package/ref-monty/crates/monty/test_cases/set__review_bugs.py +0 -35
- package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +0 -2
- package/ref-monty/crates/monty/test_cases/slice__kwargs.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__no_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__ops.py +0 -149
- package/ref-monty/crates/monty/test_cases/slice__step_zero.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/str__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_no_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_non_string.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__methods.py +0 -327
- package/ref-monty/crates/monty/test_cases/str__ops.py +0 -162
- package/ref-monty/crates/monty/test_cases/str__partition_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +0 -9
- package/ref-monty/crates/monty/test_cases/sys__types.py +0 -7
- package/ref-monty/crates/monty/test_cases/traceback__division_error.py +0 -30
- package/ref-monty/crates/monty/test_cases/traceback__index_error.py +0 -17
- package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +0 -10
- package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +0 -29
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +0 -10
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +0 -24
- package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +0 -23
- package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +0 -11
- package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +0 -16
- package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +0 -16
- package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +0 -16
- package/ref-monty/crates/monty/test_cases/try_except__all.py +0 -472
- package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +0 -2
- package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +0 -5
- package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +0 -3
- package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/tuple__methods.py +0 -19
- package/ref-monty/crates/monty/test_cases/tuple__ops.py +0 -133
- package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__ops.py +0 -200
- package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +0 -3
- package/ref-monty/crates/monty/test_cases/type__shadow_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__shadow_len.py +0 -3
- package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +0 -6
- package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/typing__types.py +0 -24
- package/ref-monty/crates/monty/test_cases/unpack__nested.py +0 -48
- package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__ops.py +0 -153
- package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__too_many.py +0 -9
- package/ref-monty/crates/monty/test_cases/version__cpython.py +0 -4
- package/ref-monty/crates/monty/test_cases/walrus__all.py +0 -178
- package/ref-monty/crates/monty/test_cases/while__all.py +0 -206
- package/ref-monty/crates/monty/tests/asyncio.rs +0 -764
- package/ref-monty/crates/monty/tests/binary_serde.rs +0 -185
- package/ref-monty/crates/monty/tests/bytecode_limits.rs +0 -248
- package/ref-monty/crates/monty/tests/datatest_runner.rs +0 -2029
- package/ref-monty/crates/monty/tests/inputs.rs +0 -420
- package/ref-monty/crates/monty/tests/json_serde.rs +0 -250
- package/ref-monty/crates/monty/tests/main.rs +0 -71
- package/ref-monty/crates/monty/tests/math_module.rs +0 -114
- package/ref-monty/crates/monty/tests/name_lookup.rs +0 -482
- package/ref-monty/crates/monty/tests/os_tests.rs +0 -459
- package/ref-monty/crates/monty/tests/parse_errors.rs +0 -441
- package/ref-monty/crates/monty/tests/print_writer.rs +0 -238
- package/ref-monty/crates/monty/tests/py_object.rs +0 -121
- package/ref-monty/crates/monty/tests/regex.rs +0 -90
- package/ref-monty/crates/monty/tests/repl.rs +0 -344
- package/ref-monty/crates/monty/tests/resource_limits.rs +0 -1826
- package/ref-monty/crates/monty/tests/try_from.rs +0 -167
- package/ref-monty/crates/monty-cli/Cargo.toml +0 -25
- package/ref-monty/crates/monty-cli/src/main.rs +0 -541
- package/ref-monty/crates/monty-js/.cargo/config.toml +0 -2
- package/ref-monty/crates/monty-js/.prettierignore +0 -8
- package/ref-monty/crates/monty-js/Cargo.toml +0 -32
- package/ref-monty/crates/monty-js/README.md +0 -207
- package/ref-monty/crates/monty-js/__test__/async.spec.ts +0 -350
- package/ref-monty/crates/monty-js/__test__/basic.spec.ts +0 -114
- package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +0 -427
- package/ref-monty/crates/monty-js/__test__/external.spec.ts +0 -354
- package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +0 -143
- package/ref-monty/crates/monty-js/__test__/limits.spec.ts +0 -162
- package/ref-monty/crates/monty-js/__test__/package.json +0 -3
- package/ref-monty/crates/monty-js/__test__/print.spec.ts +0 -229
- package/ref-monty/crates/monty-js/__test__/repl.spec.ts +0 -34
- package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +0 -205
- package/ref-monty/crates/monty-js/__test__/start.spec.ts +0 -443
- package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +0 -147
- package/ref-monty/crates/monty-js/__test__/types.spec.ts +0 -319
- package/ref-monty/crates/monty-js/build.rs +0 -61
- package/ref-monty/crates/monty-js/index-header.d.ts +0 -3
- package/ref-monty/crates/monty-js/package-lock.json +0 -4694
- package/ref-monty/crates/monty-js/package.json +0 -100
- package/ref-monty/crates/monty-js/scripts/smoke-test.sh +0 -69
- package/ref-monty/crates/monty-js/smoke-test/package.json +0 -17
- package/ref-monty/crates/monty-js/smoke-test/test.ts +0 -171
- package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +0 -11
- package/ref-monty/crates/monty-js/src/convert.rs +0 -648
- package/ref-monty/crates/monty-js/src/exceptions.rs +0 -293
- package/ref-monty/crates/monty-js/src/lib.rs +0 -41
- package/ref-monty/crates/monty-js/src/limits.rs +0 -53
- package/ref-monty/crates/monty-js/src/monty_cls.rs +0 -1407
- package/ref-monty/crates/monty-js/tsconfig.json +0 -17
- package/ref-monty/crates/monty-js/wrapper.ts +0 -701
- package/ref-monty/crates/monty-python/Cargo.toml +0 -38
- package/ref-monty/crates/monty-python/README.md +0 -134
- package/ref-monty/crates/monty-python/build.rs +0 -4
- package/ref-monty/crates/monty-python/example.py +0 -40
- package/ref-monty/crates/monty-python/exercise.py +0 -46
- package/ref-monty/crates/monty-python/pyproject.toml +0 -57
- package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +0 -281
- package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +0 -677
- package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +0 -933
- package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
- package/ref-monty/crates/monty-python/src/convert.rs +0 -273
- package/ref-monty/crates/monty-python/src/dataclass.rs +0 -461
- package/ref-monty/crates/monty-python/src/exceptions.rs +0 -557
- package/ref-monty/crates/monty-python/src/external.rs +0 -165
- package/ref-monty/crates/monty-python/src/lib.rs +0 -77
- package/ref-monty/crates/monty-python/src/limits.rs +0 -142
- package/ref-monty/crates/monty-python/src/monty_cls.rs +0 -1650
- package/ref-monty/crates/monty-python/src/repl.rs +0 -470
- package/ref-monty/crates/monty-python/src/serialization.rs +0 -761
- package/ref-monty/crates/monty-python/tests/test_async.py +0 -1201
- package/ref-monty/crates/monty-python/tests/test_basic.py +0 -66
- package/ref-monty/crates/monty-python/tests/test_dataclasses.py +0 -971
- package/ref-monty/crates/monty-python/tests/test_exceptions.py +0 -361
- package/ref-monty/crates/monty-python/tests/test_external.py +0 -367
- package/ref-monty/crates/monty-python/tests/test_inputs.py +0 -126
- package/ref-monty/crates/monty-python/tests/test_limits.py +0 -257
- package/ref-monty/crates/monty-python/tests/test_os_access.py +0 -1286
- package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +0 -731
- package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +0 -483
- package/ref-monty/crates/monty-python/tests/test_os_calls.py +0 -819
- package/ref-monty/crates/monty-python/tests/test_print.py +0 -208
- package/ref-monty/crates/monty-python/tests/test_re.py +0 -170
- package/ref-monty/crates/monty-python/tests/test_readme_examples.py +0 -20
- package/ref-monty/crates/monty-python/tests/test_repl.py +0 -749
- package/ref-monty/crates/monty-python/tests/test_serialize.py +0 -284
- package/ref-monty/crates/monty-python/tests/test_start.py +0 -346
- package/ref-monty/crates/monty-python/tests/test_threading.py +0 -163
- package/ref-monty/crates/monty-python/tests/test_type_check.py +0 -344
- package/ref-monty/crates/monty-python/tests/test_types.py +0 -553
- package/ref-monty/crates/monty-type-checking/Cargo.toml +0 -32
- package/ref-monty/crates/monty-type-checking/src/db.rs +0 -116
- package/ref-monty/crates/monty-type-checking/src/lib.rs +0 -4
- package/ref-monty/crates/monty-type-checking/src/type_check.rs +0 -280
- package/ref-monty/crates/monty-type-checking/tests/bad_types.py +0 -109
- package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +0 -21
- package/ref-monty/crates/monty-type-checking/tests/good_types.py +0 -475
- package/ref-monty/crates/monty-type-checking/tests/main.rs +0 -205
- package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +0 -56
- package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +0 -41
- package/ref-monty/crates/monty-typeshed/Cargo.toml +0 -29
- package/ref-monty/crates/monty-typeshed/README.md +0 -11
- package/ref-monty/crates/monty-typeshed/build.rs +0 -101
- package/ref-monty/crates/monty-typeshed/custom/README.md +0 -1
- package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +0 -138
- package/ref-monty/crates/monty-typeshed/custom/os.pyi +0 -87
- package/ref-monty/crates/monty-typeshed/custom/sys.pyi +0 -33
- package/ref-monty/crates/monty-typeshed/src/lib.rs +0 -56
- package/ref-monty/crates/monty-typeshed/update.py +0 -321
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +0 -1
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +0 -20
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +0 -105
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +0 -394
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +0 -138
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +0 -1434
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +0 -527
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +0 -2
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +0 -502
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +0 -376
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +0 -149
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +0 -87
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +0 -395
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +0 -8
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +0 -337
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +0 -33
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +0 -741
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +0 -1217
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +0 -716
- package/ref-monty/docs/usage-guide.md +0 -117
- package/ref-monty/examples/README.md +0 -3
- package/ref-monty/examples/expense_analysis/README.md +0 -3
- package/ref-monty/examples/expense_analysis/data.py +0 -124
- package/ref-monty/examples/expense_analysis/main.py +0 -115
- package/ref-monty/examples/sql_playground/README.md +0 -20
- package/ref-monty/examples/sql_playground/external_functions.py +0 -129
- package/ref-monty/examples/sql_playground/main.py +0 -81
- package/ref-monty/examples/sql_playground/sandbox_code.py +0 -82
- package/ref-monty/examples/sql_playground/type_stubs.pyi +0 -14
- package/ref-monty/examples/web_scraper/README.md +0 -15
- package/ref-monty/examples/web_scraper/browser.py +0 -56
- package/ref-monty/examples/web_scraper/example_code.py +0 -59
- package/ref-monty/examples/web_scraper/external_functions.py +0 -324
- package/ref-monty/examples/web_scraper/main.py +0 -193
- package/ref-monty/examples/web_scraper/sub_agent.py +0 -79
- package/ref-monty/monty-npm.md +0 -235
- package/ref-monty/pyproject.toml +0 -162
- package/ref-monty/scripts/check_imports.py +0 -91
- package/ref-monty/scripts/codecov_diff.py +0 -412
- package/ref-monty/scripts/complete_tests.py +0 -146
- package/ref-monty/scripts/flamegraph_to_text.py +0 -208
- package/ref-monty/scripts/iter_test_methods.py +0 -540
- package/ref-monty/scripts/run_traceback.py +0 -180
- package/ref-monty/scripts/startup_performance.py +0 -130
- package/ref-monty/uv.lock +0 -1779
- package/temp_resend_cli/repo/.github/scripts/pr-title-check.js +0 -34
- package/temp_resend_cli/repo/.github/workflows/ci.yml +0 -67
- package/temp_resend_cli/repo/.github/workflows/post-release.yml +0 -51
- package/temp_resend_cli/repo/.github/workflows/pr-title-check.yml +0 -13
- package/temp_resend_cli/repo/.github/workflows/release.yml +0 -175
- package/temp_resend_cli/repo/.github/workflows/test-install-unix.yml +0 -34
- package/temp_resend_cli/repo/.github/workflows/test-install-windows.yml +0 -48
- package/temp_resend_cli/repo/CHANGELOG.md +0 -31
- package/temp_resend_cli/repo/LICENSE +0 -21
- package/temp_resend_cli/repo/README.md +0 -450
- package/temp_resend_cli/repo/biome.json +0 -36
- package/temp_resend_cli/repo/install.ps1 +0 -141
- package/temp_resend_cli/repo/install.sh +0 -301
- package/temp_resend_cli/repo/package.json +0 -61
- package/temp_resend_cli/repo/pnpm-lock.yaml +0 -2439
- package/temp_resend_cli/repo/renovate.json +0 -4
- package/temp_resend_cli/repo/src/cli.ts +0 -98
- package/temp_resend_cli/repo/src/commands/api-keys/create.ts +0 -114
- package/temp_resend_cli/repo/src/commands/api-keys/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/api-keys/index.ts +0 -26
- package/temp_resend_cli/repo/src/commands/api-keys/list.ts +0 -35
- package/temp_resend_cli/repo/src/commands/api-keys/utils.ts +0 -8
- package/temp_resend_cli/repo/src/commands/auth/index.ts +0 -20
- package/temp_resend_cli/repo/src/commands/auth/login.ts +0 -234
- package/temp_resend_cli/repo/src/commands/auth/logout.ts +0 -105
- package/temp_resend_cli/repo/src/commands/broadcasts/create.ts +0 -196
- package/temp_resend_cli/repo/src/commands/broadcasts/delete.ts +0 -46
- package/temp_resend_cli/repo/src/commands/broadcasts/get.ts +0 -59
- package/temp_resend_cli/repo/src/commands/broadcasts/index.ts +0 -43
- package/temp_resend_cli/repo/src/commands/broadcasts/list.ts +0 -60
- package/temp_resend_cli/repo/src/commands/broadcasts/send.ts +0 -56
- package/temp_resend_cli/repo/src/commands/broadcasts/update.ts +0 -95
- package/temp_resend_cli/repo/src/commands/broadcasts/utils.ts +0 -35
- package/temp_resend_cli/repo/src/commands/contact-properties/create.ts +0 -118
- package/temp_resend_cli/repo/src/commands/contact-properties/delete.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contact-properties/get.ts +0 -46
- package/temp_resend_cli/repo/src/commands/contact-properties/index.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contact-properties/list.ts +0 -68
- package/temp_resend_cli/repo/src/commands/contact-properties/update.ts +0 -88
- package/temp_resend_cli/repo/src/commands/contact-properties/utils.ts +0 -17
- package/temp_resend_cli/repo/src/commands/contacts/add-segment.ts +0 -78
- package/temp_resend_cli/repo/src/commands/contacts/create.ts +0 -122
- package/temp_resend_cli/repo/src/commands/contacts/delete.ts +0 -49
- package/temp_resend_cli/repo/src/commands/contacts/get.ts +0 -53
- package/temp_resend_cli/repo/src/commands/contacts/index.ts +0 -58
- package/temp_resend_cli/repo/src/commands/contacts/list.ts +0 -57
- package/temp_resend_cli/repo/src/commands/contacts/remove-segment.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contacts/segments.ts +0 -39
- package/temp_resend_cli/repo/src/commands/contacts/topics.ts +0 -45
- package/temp_resend_cli/repo/src/commands/contacts/update-topics.ts +0 -90
- package/temp_resend_cli/repo/src/commands/contacts/update.ts +0 -77
- package/temp_resend_cli/repo/src/commands/contacts/utils.ts +0 -119
- package/temp_resend_cli/repo/src/commands/doctor.ts +0 -216
- package/temp_resend_cli/repo/src/commands/domains/create.ts +0 -83
- package/temp_resend_cli/repo/src/commands/domains/delete.ts +0 -42
- package/temp_resend_cli/repo/src/commands/domains/get.ts +0 -47
- package/temp_resend_cli/repo/src/commands/domains/index.ts +0 -35
- package/temp_resend_cli/repo/src/commands/domains/list.ts +0 -53
- package/temp_resend_cli/repo/src/commands/domains/update.ts +0 -75
- package/temp_resend_cli/repo/src/commands/domains/utils.ts +0 -44
- package/temp_resend_cli/repo/src/commands/domains/verify.ts +0 -38
- package/temp_resend_cli/repo/src/commands/emails/batch.ts +0 -140
- package/temp_resend_cli/repo/src/commands/emails/get.ts +0 -44
- package/temp_resend_cli/repo/src/commands/emails/index.ts +0 -30
- package/temp_resend_cli/repo/src/commands/emails/list.ts +0 -84
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachment.ts +0 -55
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachments.ts +0 -68
- package/temp_resend_cli/repo/src/commands/emails/receiving/get.ts +0 -58
- package/temp_resend_cli/repo/src/commands/emails/receiving/index.ts +0 -28
- package/temp_resend_cli/repo/src/commands/emails/receiving/list.ts +0 -59
- package/temp_resend_cli/repo/src/commands/emails/receiving/utils.ts +0 -38
- package/temp_resend_cli/repo/src/commands/emails/send.ts +0 -189
- package/temp_resend_cli/repo/src/commands/open.ts +0 -27
- package/temp_resend_cli/repo/src/commands/segments/create.ts +0 -50
- package/temp_resend_cli/repo/src/commands/segments/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/segments/get.ts +0 -38
- package/temp_resend_cli/repo/src/commands/segments/index.ts +0 -36
- package/temp_resend_cli/repo/src/commands/segments/list.ts +0 -58
- package/temp_resend_cli/repo/src/commands/segments/utils.ts +0 -7
- package/temp_resend_cli/repo/src/commands/teams/index.ts +0 -10
- package/temp_resend_cli/repo/src/commands/teams/list.ts +0 -35
- package/temp_resend_cli/repo/src/commands/teams/remove.ts +0 -86
- package/temp_resend_cli/repo/src/commands/teams/switch.ts +0 -76
- package/temp_resend_cli/repo/src/commands/topics/create.ts +0 -73
- package/temp_resend_cli/repo/src/commands/topics/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/topics/get.ts +0 -42
- package/temp_resend_cli/repo/src/commands/topics/index.ts +0 -42
- package/temp_resend_cli/repo/src/commands/topics/list.ts +0 -34
- package/temp_resend_cli/repo/src/commands/topics/update.ts +0 -59
- package/temp_resend_cli/repo/src/commands/topics/utils.ts +0 -16
- package/temp_resend_cli/repo/src/commands/webhooks/create.ts +0 -128
- package/temp_resend_cli/repo/src/commands/webhooks/delete.ts +0 -49
- package/temp_resend_cli/repo/src/commands/webhooks/get.ts +0 -42
- package/temp_resend_cli/repo/src/commands/webhooks/index.ts +0 -42
- package/temp_resend_cli/repo/src/commands/webhooks/list.ts +0 -55
- package/temp_resend_cli/repo/src/commands/webhooks/listen.ts +0 -379
- package/temp_resend_cli/repo/src/commands/webhooks/update.ts +0 -83
- package/temp_resend_cli/repo/src/commands/webhooks/utils.ts +0 -36
- package/temp_resend_cli/repo/src/commands/whoami.ts +0 -71
- package/temp_resend_cli/repo/src/lib/actions.ts +0 -157
- package/temp_resend_cli/repo/src/lib/client.ts +0 -37
- package/temp_resend_cli/repo/src/lib/config.ts +0 -217
- package/temp_resend_cli/repo/src/lib/files.ts +0 -15
- package/temp_resend_cli/repo/src/lib/help-text.ts +0 -38
- package/temp_resend_cli/repo/src/lib/output.ts +0 -56
- package/temp_resend_cli/repo/src/lib/pagination.ts +0 -36
- package/temp_resend_cli/repo/src/lib/prompts.ts +0 -149
- package/temp_resend_cli/repo/src/lib/spinner.ts +0 -100
- package/temp_resend_cli/repo/src/lib/table.ts +0 -57
- package/temp_resend_cli/repo/src/lib/tty.ts +0 -28
- package/temp_resend_cli/repo/src/lib/update-check.ts +0 -169
- package/temp_resend_cli/repo/src/lib/version.ts +0 -4
- package/temp_resend_cli/repo/tests/commands/api-keys/create.test.ts +0 -196
- package/temp_resend_cli/repo/tests/commands/api-keys/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/api-keys/list.test.ts +0 -134
- package/temp_resend_cli/repo/tests/commands/auth/login.test.ts +0 -153
- package/temp_resend_cli/repo/tests/commands/auth/logout.test.ts +0 -153
- package/temp_resend_cli/repo/tests/commands/broadcasts/create.test.ts +0 -454
- package/temp_resend_cli/repo/tests/commands/broadcasts/delete.test.ts +0 -183
- package/temp_resend_cli/repo/tests/commands/broadcasts/get.test.ts +0 -147
- package/temp_resend_cli/repo/tests/commands/broadcasts/list.test.ts +0 -199
- package/temp_resend_cli/repo/tests/commands/broadcasts/send.test.ts +0 -162
- package/temp_resend_cli/repo/tests/commands/broadcasts/update.test.ts +0 -288
- package/temp_resend_cli/repo/tests/commands/contact-properties/create.test.ts +0 -251
- package/temp_resend_cli/repo/tests/commands/contact-properties/delete.test.ts +0 -184
- package/temp_resend_cli/repo/tests/commands/contact-properties/get.test.ts +0 -145
- package/temp_resend_cli/repo/tests/commands/contact-properties/list.test.ts +0 -181
- package/temp_resend_cli/repo/tests/commands/contact-properties/update.test.ts +0 -217
- package/temp_resend_cli/repo/tests/commands/contacts/add-segment.test.ts +0 -189
- package/temp_resend_cli/repo/tests/commands/contacts/create.test.ts +0 -271
- package/temp_resend_cli/repo/tests/commands/contacts/delete.test.ts +0 -193
- package/temp_resend_cli/repo/tests/commands/contacts/get.test.ts +0 -149
- package/temp_resend_cli/repo/tests/commands/contacts/list.test.ts +0 -176
- package/temp_resend_cli/repo/tests/commands/contacts/remove-segment.test.ts +0 -167
- package/temp_resend_cli/repo/tests/commands/contacts/segments.test.ts +0 -168
- package/temp_resend_cli/repo/tests/commands/contacts/topics.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/contacts/update-topics.test.ts +0 -248
- package/temp_resend_cli/repo/tests/commands/contacts/update.test.ts +0 -206
- package/temp_resend_cli/repo/tests/commands/doctor.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/domains/create.test.ts +0 -193
- package/temp_resend_cli/repo/tests/commands/domains/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/domains/get.test.ts +0 -138
- package/temp_resend_cli/repo/tests/commands/domains/list.test.ts +0 -165
- package/temp_resend_cli/repo/tests/commands/domains/update.test.ts +0 -224
- package/temp_resend_cli/repo/tests/commands/domains/verify.test.ts +0 -118
- package/temp_resend_cli/repo/tests/commands/emails/batch.test.ts +0 -324
- package/temp_resend_cli/repo/tests/commands/emails/get.test.ts +0 -132
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachment.test.ts +0 -141
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachments.test.ts +0 -169
- package/temp_resend_cli/repo/tests/commands/emails/receiving/get.test.ts +0 -141
- package/temp_resend_cli/repo/tests/commands/emails/receiving/list.test.ts +0 -182
- package/temp_resend_cli/repo/tests/commands/emails/send.test.ts +0 -312
- package/temp_resend_cli/repo/tests/commands/segments/create.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/segments/delete.test.ts +0 -183
- package/temp_resend_cli/repo/tests/commands/segments/get.test.ts +0 -138
- package/temp_resend_cli/repo/tests/commands/segments/list.test.ts +0 -174
- package/temp_resend_cli/repo/tests/commands/teams/list.test.ts +0 -62
- package/temp_resend_cli/repo/tests/commands/teams/remove.test.ts +0 -110
- package/temp_resend_cli/repo/tests/commands/teams/switch.test.ts +0 -103
- package/temp_resend_cli/repo/tests/commands/topics/create.test.ts +0 -192
- package/temp_resend_cli/repo/tests/commands/topics/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/topics/get.test.ts +0 -126
- package/temp_resend_cli/repo/tests/commands/topics/list.test.ts +0 -125
- package/temp_resend_cli/repo/tests/commands/topics/update.test.ts +0 -178
- package/temp_resend_cli/repo/tests/commands/webhooks/create.test.ts +0 -225
- package/temp_resend_cli/repo/tests/commands/webhooks/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/webhooks/get.test.ts +0 -126
- package/temp_resend_cli/repo/tests/commands/webhooks/list.test.ts +0 -178
- package/temp_resend_cli/repo/tests/commands/webhooks/update.test.ts +0 -207
- package/temp_resend_cli/repo/tests/commands/whoami.test.ts +0 -98
- package/temp_resend_cli/repo/tests/e2e/smoke.test.ts +0 -93
- package/temp_resend_cli/repo/tests/helpers.ts +0 -86
- package/temp_resend_cli/repo/tests/lib/client.test.ts +0 -71
- package/temp_resend_cli/repo/tests/lib/config.test.ts +0 -451
- package/temp_resend_cli/repo/tests/lib/files.test.ts +0 -73
- package/temp_resend_cli/repo/tests/lib/help-text.test.ts +0 -97
- package/temp_resend_cli/repo/tests/lib/output.test.ts +0 -136
- package/temp_resend_cli/repo/tests/lib/prompts.test.ts +0 -185
- package/temp_resend_cli/repo/tests/lib/spinner.test.ts +0 -166
- package/temp_resend_cli/repo/tests/lib/table.test.ts +0 -63
- package/temp_resend_cli/repo/tests/lib/tty.test.ts +0 -89
- package/temp_resend_cli/repo/tests/lib/update-check.test.ts +0 -179
- package/temp_resend_cli/repo/tsconfig.json +0 -14
- package/temp_resend_cli/repo/vitest.config.e2e.ts +0 -8
- package/temp_resend_cli/repo/vitest.config.ts +0 -10
- /package/docs/{plugin-examples.md → plugins-examples.md} +0 -0
|
@@ -1,2051 +0,0 @@
|
|
|
1
|
-
/// Python string type, wrapping a Rust `String`.
|
|
2
|
-
///
|
|
3
|
-
/// This type provides Python string semantics. Currently supports basic
|
|
4
|
-
/// operations like length and equality comparison.
|
|
5
|
-
use std::{borrow::Cow, fmt};
|
|
6
|
-
use std::{cmp::Ordering, fmt::Write};
|
|
7
|
-
|
|
8
|
-
use ahash::AHashSet;
|
|
9
|
-
use smallvec::smallvec;
|
|
10
|
-
|
|
11
|
-
use super::{Bytes, MontyIter, PyTrait};
|
|
12
|
-
use crate::{
|
|
13
|
-
args::ArgValues,
|
|
14
|
-
bytecode::{CallResult, VM},
|
|
15
|
-
defer_drop, defer_drop_mut,
|
|
16
|
-
exception_private::{ExcType, RunResult},
|
|
17
|
-
heap::{DropWithHeap, Heap, HeapData, HeapGuard, HeapId},
|
|
18
|
-
intern::{StaticStrings, StringId},
|
|
19
|
-
resource::{ResourceError, ResourceTracker, check_repeat_size, check_replace_size},
|
|
20
|
-
types::Type,
|
|
21
|
-
value::{EitherStr, Value},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/// Python string value stored on the heap.
|
|
25
|
-
///
|
|
26
|
-
/// Wraps a Rust `String` and provides Python-compatible operations.
|
|
27
|
-
/// `len()` returns the number of Unicode codepoints (characters), matching Python semantics.
|
|
28
|
-
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
|
29
|
-
pub(crate) struct Str(Box<str>);
|
|
30
|
-
|
|
31
|
-
impl Str {
|
|
32
|
-
/// Creates a new Str from a Rust String.
|
|
33
|
-
#[must_use]
|
|
34
|
-
pub fn new(s: String) -> Self {
|
|
35
|
-
Self(s.into())
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/// Returns a reference to the inner string.
|
|
39
|
-
#[must_use]
|
|
40
|
-
pub fn as_str(&self) -> &str {
|
|
41
|
-
&self.0
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/// Creates a string from the `str()` constructor call.
|
|
45
|
-
///
|
|
46
|
-
/// - `str()` with no args returns an empty string
|
|
47
|
-
/// - `str(x)` converts x to its string representation using `py_str`
|
|
48
|
-
pub fn init(vm: &mut VM<'_, '_, impl ResourceTracker>, args: ArgValues) -> RunResult<Value> {
|
|
49
|
-
let value = args.get_zero_one_arg("str", vm.heap)?;
|
|
50
|
-
match value {
|
|
51
|
-
None => Ok(Value::InternString(StaticStrings::EmptyString.into())),
|
|
52
|
-
Some(v) => {
|
|
53
|
-
defer_drop!(v, vm);
|
|
54
|
-
let s = v.py_str(vm).into_owned();
|
|
55
|
-
allocate_string(s, vm.heap)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/// Handles slice-based indexing for strings.
|
|
61
|
-
///
|
|
62
|
-
/// Returns a new string containing the selected characters (Unicode-aware).
|
|
63
|
-
fn getitem_slice(&self, slice: &crate::types::Slice, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
64
|
-
let char_count = self.0.chars().count();
|
|
65
|
-
let (start, stop, step) = slice
|
|
66
|
-
.indices(char_count)
|
|
67
|
-
.map_err(|()| ExcType::value_error_slice_step_zero())?;
|
|
68
|
-
|
|
69
|
-
let result_str = get_str_slice(&self.0, start, stop, step);
|
|
70
|
-
let heap_id = heap.allocate(HeapData::Str(Self::from(result_str)))?;
|
|
71
|
-
Ok(Value::Ref(heap_id))
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
impl From<String> for Str {
|
|
76
|
-
fn from(s: String) -> Self {
|
|
77
|
-
Self(s.into())
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
impl From<&str> for Str {
|
|
82
|
-
fn from(s: &str) -> Self {
|
|
83
|
-
Self(s.into())
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
impl From<Str> for String {
|
|
88
|
-
fn from(value: Str) -> Self {
|
|
89
|
-
value.0.into_string()
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/// Allocates a string, using interned versions when possible.
|
|
94
|
-
///
|
|
95
|
-
/// Optimizations:
|
|
96
|
-
/// - Empty strings return the pre-interned `StaticStrings::EmptyString`
|
|
97
|
-
/// - Single ASCII characters return pre-interned ASCII strings
|
|
98
|
-
/// - Other strings are allocated on the heap
|
|
99
|
-
///
|
|
100
|
-
/// This avoids heap allocation for common cases like results from `strip()`,
|
|
101
|
-
/// `split()`, string iteration, etc.
|
|
102
|
-
pub fn allocate_string(s: String, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
103
|
-
match s.len() {
|
|
104
|
-
0 => Ok(Value::InternString(StaticStrings::EmptyString.into())),
|
|
105
|
-
1 => {
|
|
106
|
-
// Single byte means single ASCII character
|
|
107
|
-
let byte = s.as_bytes()[0];
|
|
108
|
-
Ok(Value::InternString(StringId::from_ascii(byte)))
|
|
109
|
-
}
|
|
110
|
-
_ => {
|
|
111
|
-
let heap_id = heap.allocate(HeapData::Str(Str::new(s)))?;
|
|
112
|
-
Ok(Value::Ref(heap_id))
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/// Allocates a single character as a string value.
|
|
118
|
-
///
|
|
119
|
-
/// ASCII characters use pre-interned strings for efficiency.
|
|
120
|
-
/// Non-ASCII characters are allocated on the heap.
|
|
121
|
-
///
|
|
122
|
-
/// This is used by string iteration and `chr()` builtin.
|
|
123
|
-
pub fn allocate_char(c: char, heap: &mut Heap<impl ResourceTracker>) -> Result<Value, ResourceError> {
|
|
124
|
-
if c.is_ascii() {
|
|
125
|
-
Ok(Value::InternString(StringId::from_ascii(c as u8)))
|
|
126
|
-
} else {
|
|
127
|
-
let heap_id = heap.allocate(HeapData::Str(Str::new(c.to_string())))?;
|
|
128
|
-
Ok(Value::Ref(heap_id))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/// Gets the character at a given index in a string, handling negative indices.
|
|
133
|
-
///
|
|
134
|
-
/// Returns `None` if the index is out of bounds. This uses a single-pass scan
|
|
135
|
-
/// to avoid allocating a `Vec<char>`.
|
|
136
|
-
///
|
|
137
|
-
/// Negative indices count from the end: -1 is the last character.
|
|
138
|
-
pub fn get_char_at_index(s: &str, index: i64) -> Option<char> {
|
|
139
|
-
let char_count = s.chars().count();
|
|
140
|
-
let len = i64::try_from(char_count).ok()?;
|
|
141
|
-
let normalized = if index < 0 { index + len } else { index };
|
|
142
|
-
|
|
143
|
-
if normalized < 0 || normalized >= len {
|
|
144
|
-
return None;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
let idx = usize::try_from(normalized).ok()?;
|
|
148
|
-
s.chars().nth(idx)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/// Extracts a slice of a string (Unicode-aware).
|
|
152
|
-
///
|
|
153
|
-
/// Handles both positive and negative step values. For negative step,
|
|
154
|
-
/// iterates backward from start down to (but not including) stop.
|
|
155
|
-
/// The `stop` parameter uses a sentinel value of `len + 1` for negative
|
|
156
|
-
/// step to indicate "go to the beginning".
|
|
157
|
-
///
|
|
158
|
-
/// Note: step must be non-zero (callers should validate this via `slice.indices()`).
|
|
159
|
-
pub(crate) fn get_str_slice(s: &str, start: usize, stop: usize, step: i64) -> String {
|
|
160
|
-
let chars: Vec<char> = s.chars().collect();
|
|
161
|
-
let mut result = String::new();
|
|
162
|
-
|
|
163
|
-
// try_from succeeds for non-negative step; step==0 rejected upstream by slice.indices()
|
|
164
|
-
if let Ok(step_usize) = usize::try_from(step) {
|
|
165
|
-
// Positive step: iterate forward
|
|
166
|
-
let mut i = start;
|
|
167
|
-
while i < stop && i < chars.len() {
|
|
168
|
-
result.push(chars[i]);
|
|
169
|
-
i += step_usize;
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
// Negative step: iterate backward
|
|
173
|
-
// start is the highest index, stop is the sentinel
|
|
174
|
-
// stop > chars.len() means "go to the beginning"
|
|
175
|
-
let step_abs = usize::try_from(-step).expect("step is negative so -step is positive");
|
|
176
|
-
let step_abs_i64 = i64::try_from(step_abs).expect("step magnitude fits in i64");
|
|
177
|
-
let mut i = i64::try_from(start).expect("start index fits in i64");
|
|
178
|
-
// stop > chars.len() is sentinel meaning "go to beginning", use -1
|
|
179
|
-
let stop_i64 = if stop > chars.len() {
|
|
180
|
-
-1
|
|
181
|
-
} else {
|
|
182
|
-
i64::try_from(stop).expect("stop bounded by chars.len() fits in i64")
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
while let Ok(i_usize) = usize::try_from(i) {
|
|
186
|
-
if i_usize >= chars.len() || i <= stop_i64 {
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
result.push(chars[i_usize]);
|
|
190
|
-
i -= step_abs_i64;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
result
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
impl std::ops::Deref for Str {
|
|
198
|
-
type Target = str;
|
|
199
|
-
|
|
200
|
-
fn deref(&self) -> &Self::Target {
|
|
201
|
-
&self.0
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
impl PyTrait for Str {
|
|
206
|
-
fn py_type(&self, _heap: &Heap<impl ResourceTracker>) -> Type {
|
|
207
|
-
Type::Str
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
fn py_estimate_size(&self) -> usize {
|
|
211
|
-
std::mem::size_of::<Self>() + self.0.len()
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
fn py_len(&self, _vm: &VM<'_, '_, impl ResourceTracker>) -> Option<usize> {
|
|
215
|
-
// Count Unicode characters, not bytes, to match Python semantics
|
|
216
|
-
Some(self.0.chars().count())
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
fn py_getitem(&self, key: &Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
220
|
-
let heap = &mut *vm.heap;
|
|
221
|
-
// Check for slice first (Value::Ref pointing to HeapData::Slice)
|
|
222
|
-
if let Value::Ref(id) = key
|
|
223
|
-
&& let HeapData::Slice(slice) = heap.get(*id)
|
|
224
|
-
{
|
|
225
|
-
// Clone the slice to release the borrow on heap before calling getitem_slice
|
|
226
|
-
let slice = slice.clone();
|
|
227
|
-
return self.getitem_slice(&slice, heap);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Extract integer index, accepting Int, Bool (True=1, False=0), and LongInt
|
|
231
|
-
let index = key.as_index(heap, Type::Str)?;
|
|
232
|
-
|
|
233
|
-
// Use single-pass indexing to avoid Vec<char> allocation
|
|
234
|
-
let c = get_char_at_index(&self.0, index).ok_or_else(ExcType::str_index_error)?;
|
|
235
|
-
Ok(allocate_char(c, heap)?)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
fn py_eq(&self, other: &Self, _vm: &mut VM<'_, '_, impl ResourceTracker>) -> Result<bool, ResourceError> {
|
|
239
|
-
Ok(self.0 == other.0)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/// Interns don't contain nested heap references.
|
|
243
|
-
fn py_dec_ref_ids(&mut self, _stack: &mut Vec<HeapId>) {
|
|
244
|
-
// No-op: strings don't hold Value references
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
fn py_bool(&self, _vm: &VM<'_, '_, impl ResourceTracker>) -> bool {
|
|
248
|
-
!self.0.is_empty()
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
fn py_cmp(
|
|
252
|
-
&self,
|
|
253
|
-
other: &Self,
|
|
254
|
-
_vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
255
|
-
) -> Result<Option<Ordering>, ResourceError> {
|
|
256
|
-
Ok(Some(self.0.cmp(&other.0)))
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
fn py_repr_fmt(
|
|
260
|
-
&self,
|
|
261
|
-
f: &mut impl Write,
|
|
262
|
-
_vm: &VM<'_, '_, impl ResourceTracker>,
|
|
263
|
-
_heap_ids: &mut AHashSet<HeapId>,
|
|
264
|
-
) -> fmt::Result {
|
|
265
|
-
string_repr_fmt(&self.0, f)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
fn py_str(&self, _vm: &VM<'_, '_, impl ResourceTracker>) -> Cow<'static, str> {
|
|
269
|
-
self.0.clone().into_string().into()
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
fn py_add(
|
|
273
|
-
&self,
|
|
274
|
-
other: &Self,
|
|
275
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
276
|
-
) -> Result<Option<Value>, crate::resource::ResourceError> {
|
|
277
|
-
let result = format!("{}{}", self.0, other.0);
|
|
278
|
-
let id = vm.heap.allocate(HeapData::Str(result.into()))?;
|
|
279
|
-
Ok(Some(Value::Ref(id)))
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
fn py_call_attr(
|
|
283
|
-
&mut self,
|
|
284
|
-
_self_id: HeapId,
|
|
285
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
286
|
-
attr: &EitherStr,
|
|
287
|
-
args: ArgValues,
|
|
288
|
-
) -> RunResult<CallResult> {
|
|
289
|
-
let args_guard = HeapGuard::new(args, vm.heap);
|
|
290
|
-
let Some(method) = attr.static_string() else {
|
|
291
|
-
return Err(ExcType::attribute_error(Type::Str, attr.as_str(vm.interns)));
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
let args = args_guard.into_inner();
|
|
295
|
-
call_str_method_impl(&self.0, method, args, vm).map(CallResult::Value)
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/// Dispatches a method call on a string value by method name.
|
|
300
|
-
///
|
|
301
|
-
/// This is the entry point for string method calls from the VM on interned strings.
|
|
302
|
-
/// Converts the `StringId` to `StaticStrings` and delegates to `call_str_method_impl`.
|
|
303
|
-
pub fn call_str_method(
|
|
304
|
-
s: &str,
|
|
305
|
-
method_id: StringId,
|
|
306
|
-
args: ArgValues,
|
|
307
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
308
|
-
) -> RunResult<Value> {
|
|
309
|
-
let args_guard = HeapGuard::new(args, vm.heap);
|
|
310
|
-
let Some(method) = StaticStrings::from_string_id(method_id) else {
|
|
311
|
-
return Err(ExcType::attribute_error(Type::Str, vm.interns.get_str(method_id)));
|
|
312
|
-
};
|
|
313
|
-
let args = args_guard.into_inner();
|
|
314
|
-
call_str_method_impl(s, method, args, vm)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/// Dispatches a method call on a string value.
|
|
318
|
-
///
|
|
319
|
-
/// This is the unified implementation for string method calls, used by both:
|
|
320
|
-
/// - `Str::py_call_attr()` for heap-allocated strings
|
|
321
|
-
/// - `call_str_method()` for interned string literals from the VM
|
|
322
|
-
///
|
|
323
|
-
/// # Not Yet Implemented
|
|
324
|
-
///
|
|
325
|
-
/// The following Python string methods are not yet implemented:
|
|
326
|
-
///
|
|
327
|
-
/// - `format()` - Requires implementing the format spec mini-language (PEP 3101),
|
|
328
|
-
/// which is complex and involves parsing format specifications like `{:>10.2f}`.
|
|
329
|
-
/// - `format_map(mapping)` - Similar to `format()` but takes a mapping; depends on
|
|
330
|
-
/// `format()` implementation.
|
|
331
|
-
/// - `maketrans()` / `translate()` - Character translation tables; moderate complexity,
|
|
332
|
-
/// requires building and applying Unicode translation maps.
|
|
333
|
-
/// - `expandtabs(tabsize=8)` - Tab expansion; simple but rarely used in practice.
|
|
334
|
-
/// - `isprintable()` - Checks if all characters are printable; requires accurate Unicode
|
|
335
|
-
/// category data for the "printable" property.
|
|
336
|
-
fn call_str_method_impl(
|
|
337
|
-
s: &str,
|
|
338
|
-
method: StaticStrings,
|
|
339
|
-
args: ArgValues,
|
|
340
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
341
|
-
) -> RunResult<Value> {
|
|
342
|
-
match method {
|
|
343
|
-
// Simple transformations (no arguments)
|
|
344
|
-
StaticStrings::Lower => {
|
|
345
|
-
args.check_zero_args("str.lower", vm.heap)?;
|
|
346
|
-
str_lower(s, vm.heap)
|
|
347
|
-
}
|
|
348
|
-
StaticStrings::Upper => {
|
|
349
|
-
args.check_zero_args("str.upper", vm.heap)?;
|
|
350
|
-
str_upper(s, vm.heap)
|
|
351
|
-
}
|
|
352
|
-
StaticStrings::Capitalize => {
|
|
353
|
-
args.check_zero_args("str.capitalize", vm.heap)?;
|
|
354
|
-
str_capitalize(s, vm.heap)
|
|
355
|
-
}
|
|
356
|
-
StaticStrings::Title => {
|
|
357
|
-
args.check_zero_args("str.title", vm.heap)?;
|
|
358
|
-
str_title(s, vm.heap)
|
|
359
|
-
}
|
|
360
|
-
StaticStrings::Swapcase => {
|
|
361
|
-
args.check_zero_args("str.swapcase", vm.heap)?;
|
|
362
|
-
str_swapcase(s, vm.heap)
|
|
363
|
-
}
|
|
364
|
-
StaticStrings::Casefold => {
|
|
365
|
-
args.check_zero_args("str.casefold", vm.heap)?;
|
|
366
|
-
str_casefold(s, vm.heap)
|
|
367
|
-
}
|
|
368
|
-
// Predicate methods (no arguments, return bool)
|
|
369
|
-
StaticStrings::Isalpha => {
|
|
370
|
-
args.check_zero_args("str.isalpha", vm.heap)?;
|
|
371
|
-
Ok(Value::Bool(str_isalpha(s)))
|
|
372
|
-
}
|
|
373
|
-
StaticStrings::Isdigit => {
|
|
374
|
-
args.check_zero_args("str.isdigit", vm.heap)?;
|
|
375
|
-
Ok(Value::Bool(str_isdigit(s)))
|
|
376
|
-
}
|
|
377
|
-
StaticStrings::Isalnum => {
|
|
378
|
-
args.check_zero_args("str.isalnum", vm.heap)?;
|
|
379
|
-
Ok(Value::Bool(str_isalnum(s)))
|
|
380
|
-
}
|
|
381
|
-
StaticStrings::Isnumeric => {
|
|
382
|
-
args.check_zero_args("str.isnumeric", vm.heap)?;
|
|
383
|
-
Ok(Value::Bool(str_isnumeric(s)))
|
|
384
|
-
}
|
|
385
|
-
StaticStrings::Isspace => {
|
|
386
|
-
args.check_zero_args("str.isspace", vm.heap)?;
|
|
387
|
-
Ok(Value::Bool(str_isspace(s)))
|
|
388
|
-
}
|
|
389
|
-
StaticStrings::Islower => {
|
|
390
|
-
args.check_zero_args("str.islower", vm.heap)?;
|
|
391
|
-
Ok(Value::Bool(str_islower(s)))
|
|
392
|
-
}
|
|
393
|
-
StaticStrings::Isupper => {
|
|
394
|
-
args.check_zero_args("str.isupper", vm.heap)?;
|
|
395
|
-
Ok(Value::Bool(str_isupper(s)))
|
|
396
|
-
}
|
|
397
|
-
StaticStrings::Isascii => {
|
|
398
|
-
args.check_zero_args("str.isascii", vm.heap)?;
|
|
399
|
-
Ok(Value::Bool(s.is_ascii()))
|
|
400
|
-
}
|
|
401
|
-
StaticStrings::Isdecimal => {
|
|
402
|
-
args.check_zero_args("str.isdecimal", vm.heap)?;
|
|
403
|
-
Ok(Value::Bool(str_isdecimal(s)))
|
|
404
|
-
}
|
|
405
|
-
// Search methods
|
|
406
|
-
StaticStrings::Find => str_find(s, args, vm),
|
|
407
|
-
StaticStrings::Rfind => str_rfind(s, args, vm),
|
|
408
|
-
StaticStrings::Index => str_index(s, args, vm),
|
|
409
|
-
StaticStrings::Rindex => str_rindex(s, args, vm),
|
|
410
|
-
StaticStrings::Count => str_count(s, args, vm),
|
|
411
|
-
StaticStrings::Startswith => str_startswith(s, args, vm),
|
|
412
|
-
StaticStrings::Endswith => str_endswith(s, args, vm),
|
|
413
|
-
// Strip/trim methods
|
|
414
|
-
StaticStrings::Strip => str_strip(s, args, vm),
|
|
415
|
-
StaticStrings::Lstrip => str_lstrip(s, args, vm),
|
|
416
|
-
StaticStrings::Rstrip => str_rstrip(s, args, vm),
|
|
417
|
-
StaticStrings::Removeprefix => str_removeprefix(s, args, vm),
|
|
418
|
-
StaticStrings::Removesuffix => str_removesuffix(s, args, vm),
|
|
419
|
-
// Split methods
|
|
420
|
-
StaticStrings::Split => str_split(s, args, vm),
|
|
421
|
-
StaticStrings::Rsplit => str_rsplit(s, args, vm),
|
|
422
|
-
StaticStrings::Splitlines => str_splitlines(s, args, vm),
|
|
423
|
-
StaticStrings::Partition => str_partition(s, args, vm),
|
|
424
|
-
StaticStrings::Rpartition => str_rpartition(s, args, vm),
|
|
425
|
-
// Replace/modify methods
|
|
426
|
-
StaticStrings::Replace => str_replace(s, args, vm),
|
|
427
|
-
StaticStrings::Center => str_center(s, args, vm),
|
|
428
|
-
StaticStrings::Ljust => str_ljust(s, args, vm),
|
|
429
|
-
StaticStrings::Rjust => str_rjust(s, args, vm),
|
|
430
|
-
StaticStrings::Zfill => str_zfill(s, args, vm.heap),
|
|
431
|
-
// Additional methods
|
|
432
|
-
StaticStrings::Encode => str_encode(s, args, vm),
|
|
433
|
-
StaticStrings::Isidentifier => {
|
|
434
|
-
args.check_zero_args("str.isidentifier", vm.heap)?;
|
|
435
|
-
Ok(Value::Bool(str_isidentifier(s)))
|
|
436
|
-
}
|
|
437
|
-
StaticStrings::Istitle => {
|
|
438
|
-
args.check_zero_args("str.istitle", vm.heap)?;
|
|
439
|
-
Ok(Value::Bool(str_istitle(s)))
|
|
440
|
-
}
|
|
441
|
-
// Existing method
|
|
442
|
-
StaticStrings::Join => {
|
|
443
|
-
let iterable = args.get_one_arg("str.join", vm.heap)?;
|
|
444
|
-
str_join(s, iterable, vm)
|
|
445
|
-
}
|
|
446
|
-
_ => {
|
|
447
|
-
args.drop_with_heap(vm.heap);
|
|
448
|
-
Err(ExcType::attribute_error(Type::Str, method.into()))
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/// Implements Python's `str.join(iterable)` method.
|
|
454
|
-
///
|
|
455
|
-
/// Joins elements of the iterable with the separator string, returning
|
|
456
|
-
/// a new heap-allocated string. Each element must be a string.
|
|
457
|
-
///
|
|
458
|
-
/// # Arguments
|
|
459
|
-
/// * `separator` - The separator string (e.g., "," for comma-separated)
|
|
460
|
-
/// * `iterable` - The iterable containing string elements to join
|
|
461
|
-
/// * `heap` - The heap for allocation and reference counting
|
|
462
|
-
/// * `interns` - The interns table for resolving interned strings
|
|
463
|
-
///
|
|
464
|
-
/// # Errors
|
|
465
|
-
/// Returns `TypeError` if the argument is not iterable or if any element is not a string.
|
|
466
|
-
fn str_join(separator: &str, iterable: Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
467
|
-
// Create MontyIter from the iterable, with join-specific error message
|
|
468
|
-
let Ok(iter) = MontyIter::new(iterable, vm) else {
|
|
469
|
-
return Err(ExcType::type_error_join_not_iterable());
|
|
470
|
-
};
|
|
471
|
-
defer_drop_mut!(iter, vm);
|
|
472
|
-
|
|
473
|
-
// Build result string, tracking index for error messages
|
|
474
|
-
let mut result = String::new();
|
|
475
|
-
let mut index = 0usize;
|
|
476
|
-
|
|
477
|
-
while let Some(item) = iter.for_next(vm)? {
|
|
478
|
-
defer_drop!(item, vm);
|
|
479
|
-
if index > 0 {
|
|
480
|
-
result.push_str(separator);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Check item is a string and extract its content
|
|
484
|
-
match item {
|
|
485
|
-
Value::InternString(id) => {
|
|
486
|
-
result.push_str(vm.interns.get_str(*id));
|
|
487
|
-
}
|
|
488
|
-
Value::Ref(heap_id) => {
|
|
489
|
-
if let HeapData::Str(s) = vm.heap.get(*heap_id) {
|
|
490
|
-
result.push_str(s.as_str());
|
|
491
|
-
} else {
|
|
492
|
-
let t = item.py_type(vm.heap);
|
|
493
|
-
return Err(ExcType::type_error_join_item(index, t));
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
_ => {
|
|
497
|
-
let t = item.py_type(vm.heap);
|
|
498
|
-
return Err(ExcType::type_error_join_item(index, t));
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
index += 1;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Allocate result (uses interned empty string if result is empty)
|
|
505
|
-
allocate_string(result, vm.heap)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/// Writes a Python repr() string for a given string slice to a formatter.
|
|
509
|
-
///
|
|
510
|
-
/// Chooses between single and double quotes based on the string content:
|
|
511
|
-
/// - Uses double quotes if the string contains single quotes but not double quotes
|
|
512
|
-
/// - Uses single quotes by default, escaping any contained single quotes
|
|
513
|
-
///
|
|
514
|
-
/// Common escape sequences (backslash, newline, tab, carriage return) are always escaped.
|
|
515
|
-
pub fn string_repr_fmt(s: &str, f: &mut impl Write) -> fmt::Result {
|
|
516
|
-
// Check if the string contains single quotes but not double quotes
|
|
517
|
-
if s.contains('\'') && !s.contains('"') {
|
|
518
|
-
// Use double quotes if string contains only single quotes
|
|
519
|
-
f.write_char('"')?;
|
|
520
|
-
for c in s.chars() {
|
|
521
|
-
match c {
|
|
522
|
-
'\\' => f.write_str("\\\\")?,
|
|
523
|
-
'\n' => f.write_str("\\n")?,
|
|
524
|
-
'\t' => f.write_str("\\t")?,
|
|
525
|
-
'\r' => f.write_str("\\r")?,
|
|
526
|
-
_ => f.write_char(c)?,
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
f.write_char('"')
|
|
530
|
-
} else {
|
|
531
|
-
// Use single quotes by default, escape any single quotes in the string
|
|
532
|
-
f.write_char('\'')?;
|
|
533
|
-
for c in s.chars() {
|
|
534
|
-
match c {
|
|
535
|
-
'\\' => f.write_str("\\\\")?,
|
|
536
|
-
'\n' => f.write_str("\\n")?,
|
|
537
|
-
'\t' => f.write_str("\\t")?,
|
|
538
|
-
'\r' => f.write_str("\\r")?,
|
|
539
|
-
'\'' => f.write_str("\\'")?,
|
|
540
|
-
_ => f.write_char(c)?,
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
f.write_char('\'')
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/// Formatter for a Python repr() string.
|
|
548
|
-
#[derive(Debug)]
|
|
549
|
-
pub struct StringRepr<'a>(pub &'a str);
|
|
550
|
-
|
|
551
|
-
impl fmt::Display for StringRepr<'_> {
|
|
552
|
-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
553
|
-
string_repr_fmt(self.0, f)
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// =============================================================================
|
|
558
|
-
// Simple transformations (no arguments)
|
|
559
|
-
// =============================================================================
|
|
560
|
-
|
|
561
|
-
/// Implements Python's `str.lower()` method.
|
|
562
|
-
fn str_lower(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
563
|
-
allocate_string(s.to_lowercase(), heap)
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/// Implements Python's `str.upper()` method.
|
|
567
|
-
fn str_upper(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
568
|
-
allocate_string(s.to_uppercase(), heap)
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/// Implements Python's `str.capitalize()` method.
|
|
572
|
-
///
|
|
573
|
-
/// Returns a copy of the string with its first character capitalized and the rest lowercased.
|
|
574
|
-
fn str_capitalize(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
575
|
-
let mut chars = s.chars();
|
|
576
|
-
let result = match chars.next() {
|
|
577
|
-
None => String::new(),
|
|
578
|
-
Some(first) => {
|
|
579
|
-
let mut result = first.to_uppercase().to_string();
|
|
580
|
-
for c in chars {
|
|
581
|
-
result.extend(c.to_lowercase());
|
|
582
|
-
}
|
|
583
|
-
result
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
allocate_string(result, heap)
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/// Implements Python's `str.title()` method.
|
|
590
|
-
///
|
|
591
|
-
/// Returns a titlecased version of the string where words start with an uppercase
|
|
592
|
-
/// character and the remaining characters are lowercase.
|
|
593
|
-
fn str_title(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
594
|
-
let mut result = String::with_capacity(s.len());
|
|
595
|
-
let mut prev_is_cased = false;
|
|
596
|
-
|
|
597
|
-
for c in s.chars() {
|
|
598
|
-
if prev_is_cased {
|
|
599
|
-
result.extend(c.to_lowercase());
|
|
600
|
-
} else {
|
|
601
|
-
result.extend(c.to_uppercase());
|
|
602
|
-
}
|
|
603
|
-
prev_is_cased = c.is_alphabetic();
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
allocate_string(result, heap)
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/// Implements Python's `str.swapcase()` method.
|
|
610
|
-
///
|
|
611
|
-
/// Returns a copy of the string with uppercase characters converted to lowercase and vice versa.
|
|
612
|
-
fn str_swapcase(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
613
|
-
let mut result = String::with_capacity(s.len());
|
|
614
|
-
|
|
615
|
-
for c in s.chars() {
|
|
616
|
-
if c.is_uppercase() {
|
|
617
|
-
result.extend(c.to_lowercase());
|
|
618
|
-
} else if c.is_lowercase() {
|
|
619
|
-
result.extend(c.to_uppercase());
|
|
620
|
-
} else {
|
|
621
|
-
result.push(c);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
allocate_string(result, heap)
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/// Implements Python's `str.casefold()` method.
|
|
629
|
-
///
|
|
630
|
-
/// Returns a casefolded copy of the string. Casefolding is similar to lowercasing
|
|
631
|
-
/// but more aggressive because it is intended for caseless string matching.
|
|
632
|
-
fn str_casefold(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
633
|
-
// Rust's to_lowercase() is equivalent to Unicode casefolding for most purposes
|
|
634
|
-
allocate_string(s.to_lowercase(), heap)
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// =============================================================================
|
|
638
|
-
// Predicate methods (no arguments, return bool)
|
|
639
|
-
// =============================================================================
|
|
640
|
-
|
|
641
|
-
/// Implements Python's `str.isalpha()` method.
|
|
642
|
-
///
|
|
643
|
-
/// Returns True if all characters in the string are alphabetic and there is at least one character.
|
|
644
|
-
fn str_isalpha(s: &str) -> bool {
|
|
645
|
-
!s.is_empty() && s.chars().all(char::is_alphabetic)
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/// Implements Python's `str.isdigit()` method.
|
|
649
|
-
///
|
|
650
|
-
/// Returns True if all characters in the string are digits and there is at least one character.
|
|
651
|
-
/// In Python, digits include decimal digits (Nd) plus characters with Numeric_Type=Digit
|
|
652
|
-
/// (superscripts, subscripts, circled digits, etc.).
|
|
653
|
-
fn str_isdigit(s: &str) -> bool {
|
|
654
|
-
!s.is_empty() && s.chars().all(is_unicode_digit)
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/// Implements Python's `str.isalnum()` method.
|
|
658
|
-
///
|
|
659
|
-
/// Returns True if all characters in the string are alphanumeric and there is at least one character.
|
|
660
|
-
fn str_isalnum(s: &str) -> bool {
|
|
661
|
-
!s.is_empty() && s.chars().all(char::is_alphanumeric)
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/// Implements Python's `str.isnumeric()` method.
|
|
665
|
-
///
|
|
666
|
-
/// Returns True if all characters in the string are numeric and there is at least one character.
|
|
667
|
-
/// In Python, numeric includes decimal digits (Nd), letter numerals (Nl), and other numerals (No).
|
|
668
|
-
/// Rust's `char::is_numeric()` checks for all of these categories.
|
|
669
|
-
fn str_isnumeric(s: &str) -> bool {
|
|
670
|
-
!s.is_empty() && s.chars().all(char::is_numeric)
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/// Implements Python's `str.isspace()` method.
|
|
674
|
-
///
|
|
675
|
-
/// Returns True if all characters in the string are whitespace and there is at least one character.
|
|
676
|
-
fn str_isspace(s: &str) -> bool {
|
|
677
|
-
!s.is_empty() && s.chars().all(char::is_whitespace)
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
/// Implements Python's `str.islower()` method.
|
|
681
|
-
///
|
|
682
|
-
/// Returns True if all cased characters in the string are lowercase and there is at least one cased character.
|
|
683
|
-
fn str_islower(s: &str) -> bool {
|
|
684
|
-
let mut has_cased = false;
|
|
685
|
-
for c in s.chars() {
|
|
686
|
-
if c.is_uppercase() {
|
|
687
|
-
return false;
|
|
688
|
-
}
|
|
689
|
-
if c.is_lowercase() {
|
|
690
|
-
has_cased = true;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
has_cased
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/// Implements Python's `str.isupper()` method.
|
|
697
|
-
///
|
|
698
|
-
/// Returns True if all cased characters in the string are uppercase and there is at least one cased character.
|
|
699
|
-
fn str_isupper(s: &str) -> bool {
|
|
700
|
-
let mut has_cased = false;
|
|
701
|
-
for c in s.chars() {
|
|
702
|
-
if c.is_lowercase() {
|
|
703
|
-
return false;
|
|
704
|
-
}
|
|
705
|
-
if c.is_uppercase() {
|
|
706
|
-
has_cased = true;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
has_cased
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/// Implements Python's `str.isdecimal()` method.
|
|
713
|
-
///
|
|
714
|
-
/// Returns True if all characters in the string are decimal characters and there is at least one character.
|
|
715
|
-
/// Decimal characters are those in Unicode category Nd (Decimal_Number) - digits that can be used
|
|
716
|
-
/// to form numbers in base 10.
|
|
717
|
-
fn str_isdecimal(s: &str) -> bool {
|
|
718
|
-
!s.is_empty() && s.chars().all(is_unicode_decimal)
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/// Checks if a character is a Unicode decimal digit (Nd category).
|
|
722
|
-
///
|
|
723
|
-
/// This covers decimal digit ranges from various scripts including ASCII, Arabic-Indic,
|
|
724
|
-
/// Devanagari, Bengali, Thai, Fullwidth, and many others.
|
|
725
|
-
fn is_unicode_decimal(c: char) -> bool {
|
|
726
|
-
let cp = c as u32;
|
|
727
|
-
matches!(
|
|
728
|
-
cp,
|
|
729
|
-
// Basic Latin (ASCII digits)
|
|
730
|
-
0x0030..=0x0039
|
|
731
|
-
// Arabic-Indic digits
|
|
732
|
-
| 0x0660..=0x0669
|
|
733
|
-
// Extended Arabic-Indic digits
|
|
734
|
-
| 0x06F0..=0x06F9
|
|
735
|
-
// NKo digits
|
|
736
|
-
| 0x07C0..=0x07C9
|
|
737
|
-
// Devanagari digits
|
|
738
|
-
| 0x0966..=0x096F
|
|
739
|
-
// Bengali digits
|
|
740
|
-
| 0x09E6..=0x09EF
|
|
741
|
-
// Gurmukhi digits
|
|
742
|
-
| 0x0A66..=0x0A6F
|
|
743
|
-
// Gujarati digits
|
|
744
|
-
| 0x0AE6..=0x0AEF
|
|
745
|
-
// Oriya digits
|
|
746
|
-
| 0x0B66..=0x0B6F
|
|
747
|
-
// Tamil digits
|
|
748
|
-
| 0x0BE6..=0x0BEF
|
|
749
|
-
// Telugu digits
|
|
750
|
-
| 0x0C66..=0x0C6F
|
|
751
|
-
// Kannada digits
|
|
752
|
-
| 0x0CE6..=0x0CEF
|
|
753
|
-
// Malayalam digits
|
|
754
|
-
| 0x0D66..=0x0D6F
|
|
755
|
-
// Sinhala Lith digits
|
|
756
|
-
| 0x0DE6..=0x0DEF
|
|
757
|
-
// Thai digits
|
|
758
|
-
| 0x0E50..=0x0E59
|
|
759
|
-
// Lao digits
|
|
760
|
-
| 0x0ED0..=0x0ED9
|
|
761
|
-
// Tibetan digits
|
|
762
|
-
| 0x0F20..=0x0F29
|
|
763
|
-
// Myanmar digits
|
|
764
|
-
| 0x1040..=0x1049
|
|
765
|
-
// Myanmar Shan digits
|
|
766
|
-
| 0x1090..=0x1099
|
|
767
|
-
// Khmer digits
|
|
768
|
-
| 0x17E0..=0x17E9
|
|
769
|
-
// Mongolian digits
|
|
770
|
-
| 0x1810..=0x1819
|
|
771
|
-
// Limbu digits
|
|
772
|
-
| 0x1946..=0x194F
|
|
773
|
-
// New Tai Lue digits
|
|
774
|
-
| 0x19D0..=0x19D9
|
|
775
|
-
// Tai Tham Hora digits
|
|
776
|
-
| 0x1A80..=0x1A89
|
|
777
|
-
// Tai Tham Tham digits
|
|
778
|
-
| 0x1A90..=0x1A99
|
|
779
|
-
// Balinese digits
|
|
780
|
-
| 0x1B50..=0x1B59
|
|
781
|
-
// Sundanese digits
|
|
782
|
-
| 0x1BB0..=0x1BB9
|
|
783
|
-
// Lepcha digits
|
|
784
|
-
| 0x1C40..=0x1C49
|
|
785
|
-
// Ol Chiki digits
|
|
786
|
-
| 0x1C50..=0x1C59
|
|
787
|
-
// Vai digits
|
|
788
|
-
| 0xA620..=0xA629
|
|
789
|
-
// Saurashtra digits
|
|
790
|
-
| 0xA8D0..=0xA8D9
|
|
791
|
-
// Kayah Li digits
|
|
792
|
-
| 0xA900..=0xA909
|
|
793
|
-
// Javanese digits
|
|
794
|
-
| 0xA9D0..=0xA9D9
|
|
795
|
-
// Myanmar Tai Laing digits
|
|
796
|
-
| 0xA9F0..=0xA9F9
|
|
797
|
-
// Cham digits
|
|
798
|
-
| 0xAA50..=0xAA59
|
|
799
|
-
// Meetei Mayek digits
|
|
800
|
-
| 0xABF0..=0xABF9
|
|
801
|
-
// Fullwidth digits
|
|
802
|
-
| 0xFF10..=0xFF19
|
|
803
|
-
// Osmanya digits
|
|
804
|
-
| 0x104A0..=0x104A9
|
|
805
|
-
// Hanifi Rohingya digits
|
|
806
|
-
| 0x10D30..=0x10D39
|
|
807
|
-
// Brahmi digits
|
|
808
|
-
| 0x11066..=0x1106F
|
|
809
|
-
// Sora Sompeng digits
|
|
810
|
-
| 0x110F0..=0x110F9
|
|
811
|
-
// Chakma digits
|
|
812
|
-
| 0x11136..=0x1113F
|
|
813
|
-
// Sharada digits
|
|
814
|
-
| 0x111D0..=0x111D9
|
|
815
|
-
// Khudawadi digits
|
|
816
|
-
| 0x112F0..=0x112F9
|
|
817
|
-
// Newa digits
|
|
818
|
-
| 0x11450..=0x11459
|
|
819
|
-
// Tirhuta digits
|
|
820
|
-
| 0x114D0..=0x114D9
|
|
821
|
-
// Modi digits
|
|
822
|
-
| 0x11650..=0x11659
|
|
823
|
-
// Takri digits
|
|
824
|
-
| 0x116C0..=0x116C9
|
|
825
|
-
// Ahom digits
|
|
826
|
-
| 0x11730..=0x11739
|
|
827
|
-
// Warang Citi digits
|
|
828
|
-
| 0x118E0..=0x118E9
|
|
829
|
-
// Dives Akuru digits
|
|
830
|
-
| 0x11950..=0x11959
|
|
831
|
-
// Bhaiksuki digits
|
|
832
|
-
| 0x11C50..=0x11C59
|
|
833
|
-
// Masaram Gondi digits
|
|
834
|
-
| 0x11D50..=0x11D59
|
|
835
|
-
// Gunjala Gondi digits
|
|
836
|
-
| 0x11DA0..=0x11DA9
|
|
837
|
-
// Adlam digits
|
|
838
|
-
| 0x1E950..=0x1E959
|
|
839
|
-
// Segmented digits
|
|
840
|
-
| 0x1FBF0..=0x1FBF9
|
|
841
|
-
)
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
/// Checks if a character is a Unicode digit (isdigit).
|
|
845
|
-
///
|
|
846
|
-
/// This includes decimal digits (Nd) plus characters with Numeric_Type=Digit
|
|
847
|
-
/// such as superscripts, subscripts, and circled digits.
|
|
848
|
-
fn is_unicode_digit(c: char) -> bool {
|
|
849
|
-
// First check if it's a decimal digit
|
|
850
|
-
if is_unicode_decimal(c) {
|
|
851
|
-
return true;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
let cp = c as u32;
|
|
855
|
-
matches!(
|
|
856
|
-
cp,
|
|
857
|
-
// Superscripts (², ³)
|
|
858
|
-
0x00B2..=0x00B3
|
|
859
|
-
// Superscript 1
|
|
860
|
-
| 0x00B9
|
|
861
|
-
// Superscript digits 0, 4-9
|
|
862
|
-
| 0x2070
|
|
863
|
-
| 0x2074..=0x2079
|
|
864
|
-
// Subscript digits 0-9
|
|
865
|
-
| 0x2080..=0x2089
|
|
866
|
-
// Circled digits 1-9
|
|
867
|
-
| 0x2460..=0x2468
|
|
868
|
-
// Circled digit 0
|
|
869
|
-
| 0x24EA
|
|
870
|
-
// Circled digits 10-20
|
|
871
|
-
| 0x2469..=0x2473
|
|
872
|
-
// Parenthesized digits 1-9
|
|
873
|
-
| 0x2474..=0x247C
|
|
874
|
-
// Period digits 1-9
|
|
875
|
-
| 0x2488..=0x2490
|
|
876
|
-
// Double circled digits 1-10
|
|
877
|
-
| 0x24F5..=0x24FE
|
|
878
|
-
// Dingbat circled sans-serif digits 1-10
|
|
879
|
-
| 0x2780..=0x2789
|
|
880
|
-
// Dingbat negative circled digits 1-10
|
|
881
|
-
| 0x278A..=0x2793
|
|
882
|
-
// Dingbat circled sans-serif digits 1-10
|
|
883
|
-
| 0x24FF
|
|
884
|
-
// Fullwidth digit zero (already in decimal, but include for completeness)
|
|
885
|
-
// | 0xFF10..=0xFF19 // Already covered by is_unicode_decimal
|
|
886
|
-
)
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// =============================================================================
|
|
890
|
-
// Search methods
|
|
891
|
-
// =============================================================================
|
|
892
|
-
|
|
893
|
-
/// Implements Python's `str.find(sub, start?, end?)` method.
|
|
894
|
-
///
|
|
895
|
-
/// Returns the lowest index in the string where substring sub is found within
|
|
896
|
-
/// the slice s[start:end]. Returns -1 if sub is not found.
|
|
897
|
-
fn str_find(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
898
|
-
let (sub, start, end) = parse_search_args("str.find", s, args, vm)?;
|
|
899
|
-
let slice = slice_string(s, start, end);
|
|
900
|
-
let result = match slice.find(&sub) {
|
|
901
|
-
Some(pos) => {
|
|
902
|
-
// Convert byte offset to char offset, then add start offset
|
|
903
|
-
let char_pos = slice[..pos].chars().count();
|
|
904
|
-
i64::try_from(start + char_pos).unwrap_or(i64::MAX)
|
|
905
|
-
}
|
|
906
|
-
None => -1,
|
|
907
|
-
};
|
|
908
|
-
Ok(Value::Int(result))
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
/// Implements Python's `str.rfind(sub, start?, end?)` method.
|
|
912
|
-
///
|
|
913
|
-
/// Returns the highest index in the string where substring sub is found within
|
|
914
|
-
/// the slice s[start:end]. Returns -1 if sub is not found.
|
|
915
|
-
fn str_rfind(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
916
|
-
let (sub, start, end) = parse_search_args("str.rfind", s, args, vm)?;
|
|
917
|
-
let slice = slice_string(s, start, end);
|
|
918
|
-
let result = match slice.rfind(&sub) {
|
|
919
|
-
Some(pos) => {
|
|
920
|
-
// Convert byte offset to char offset, then add start offset
|
|
921
|
-
let char_pos = slice[..pos].chars().count();
|
|
922
|
-
i64::try_from(start + char_pos).unwrap_or(i64::MAX)
|
|
923
|
-
}
|
|
924
|
-
None => -1,
|
|
925
|
-
};
|
|
926
|
-
Ok(Value::Int(result))
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
/// Implements Python's `str.index(sub, start?, end?)` method.
|
|
930
|
-
///
|
|
931
|
-
/// Like find(), but raises ValueError when the substring is not found.
|
|
932
|
-
fn str_index(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
933
|
-
let (sub, start, end) = parse_search_args("str.index", s, args, vm)?;
|
|
934
|
-
let slice = slice_string(s, start, end);
|
|
935
|
-
match slice.find(&sub) {
|
|
936
|
-
Some(pos) => {
|
|
937
|
-
let char_pos = slice[..pos].chars().count();
|
|
938
|
-
let result = i64::try_from(start + char_pos).unwrap_or(i64::MAX);
|
|
939
|
-
Ok(Value::Int(result))
|
|
940
|
-
}
|
|
941
|
-
None => Err(ExcType::value_error_substring_not_found()),
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
/// Implements Python's `str.rindex(sub, start?, end?)` method.
|
|
946
|
-
///
|
|
947
|
-
/// Like rfind(), but raises ValueError when the substring is not found.
|
|
948
|
-
fn str_rindex(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
949
|
-
let (sub, start, end) = parse_search_args("str.rindex", s, args, vm)?;
|
|
950
|
-
let slice = slice_string(s, start, end);
|
|
951
|
-
match slice.rfind(&sub) {
|
|
952
|
-
Some(pos) => {
|
|
953
|
-
let char_pos = slice[..pos].chars().count();
|
|
954
|
-
let result = i64::try_from(start + char_pos).unwrap_or(i64::MAX);
|
|
955
|
-
Ok(Value::Int(result))
|
|
956
|
-
}
|
|
957
|
-
None => Err(ExcType::value_error_substring_not_found()),
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
/// Implements Python's `str.count(sub, start?, end?)` method.
|
|
962
|
-
///
|
|
963
|
-
/// Returns the number of non-overlapping occurrences of substring sub in
|
|
964
|
-
/// the string s[start:end].
|
|
965
|
-
fn str_count(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
966
|
-
let (sub, start, end) = parse_search_args("str.count", s, args, vm)?;
|
|
967
|
-
let slice = slice_string(s, start, end);
|
|
968
|
-
let count = if sub.is_empty() {
|
|
969
|
-
// Empty string matches between every character, plus start and end
|
|
970
|
-
slice.chars().count() + 1
|
|
971
|
-
} else {
|
|
972
|
-
slice.matches(&sub).count()
|
|
973
|
-
};
|
|
974
|
-
let result = i64::try_from(count).unwrap_or(i64::MAX);
|
|
975
|
-
Ok(Value::Int(result))
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
/// Implements Python's `str.startswith(prefix, start?, end?)` method.
|
|
979
|
-
///
|
|
980
|
-
/// Returns True if the string starts with the prefix, otherwise returns False.
|
|
981
|
-
/// The prefix argument can be a string or a tuple of strings.
|
|
982
|
-
fn str_startswith(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
983
|
-
let (prefixes, start, end) = parse_prefix_suffix_args("str.startswith", s, args, vm)?;
|
|
984
|
-
let slice = slice_string(s, start, end);
|
|
985
|
-
let result = prefixes.iter().any(|prefix| slice.starts_with(prefix));
|
|
986
|
-
Ok(Value::Bool(result))
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
/// Implements Python's `str.endswith(suffix, start?, end?)` method.
|
|
990
|
-
///
|
|
991
|
-
/// Returns True if the string ends with the suffix, otherwise returns False.
|
|
992
|
-
/// The suffix argument can be a string or a tuple of strings.
|
|
993
|
-
fn str_endswith(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
994
|
-
let (suffixes, start, end) = parse_prefix_suffix_args("str.endswith", s, args, vm)?;
|
|
995
|
-
let slice = slice_string(s, start, end);
|
|
996
|
-
let result = suffixes.iter().any(|suffix| slice.ends_with(suffix));
|
|
997
|
-
Ok(Value::Bool(result))
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
/// Parses arguments for search methods (find, rfind, index, rindex, count, startswith, endswith).
|
|
1001
|
-
///
|
|
1002
|
-
/// Returns (substring, start, end) where start and end are character indices.
|
|
1003
|
-
fn parse_search_args(
|
|
1004
|
-
method: &str,
|
|
1005
|
-
s: &str,
|
|
1006
|
-
args: ArgValues,
|
|
1007
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
1008
|
-
) -> RunResult<(String, usize, usize)> {
|
|
1009
|
-
let pos = args.into_pos_only(method, vm.heap)?;
|
|
1010
|
-
defer_drop!(pos, vm);
|
|
1011
|
-
|
|
1012
|
-
let str_len = s.chars().count();
|
|
1013
|
-
match pos.as_slice() {
|
|
1014
|
-
[sub_value] => {
|
|
1015
|
-
let sub = extract_string_arg(sub_value, vm)?;
|
|
1016
|
-
Ok((sub, 0, str_len))
|
|
1017
|
-
}
|
|
1018
|
-
[sub_value, start_value] => {
|
|
1019
|
-
let sub = extract_string_arg(sub_value, vm)?;
|
|
1020
|
-
let start = optional_index(start_value, 0, str_len, vm.heap)?;
|
|
1021
|
-
Ok((sub, start, str_len))
|
|
1022
|
-
}
|
|
1023
|
-
[sub_value, start_value, end_value] => {
|
|
1024
|
-
let sub = extract_string_arg(sub_value, vm)?;
|
|
1025
|
-
let start = optional_index(start_value, 0, str_len, vm.heap)?;
|
|
1026
|
-
let end = optional_index(end_value, str_len, str_len, vm.heap)?;
|
|
1027
|
-
Ok((sub, start, end))
|
|
1028
|
-
}
|
|
1029
|
-
[] => Err(ExcType::type_error_at_least(method, 1, 0)),
|
|
1030
|
-
_ => Err(ExcType::type_error_at_most(method, 3, pos.len())),
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
/// Parses arguments for startswith/endswith methods.
|
|
1035
|
-
///
|
|
1036
|
-
/// Returns (prefixes/suffixes as Vec, start, end) where start and end are character indices.
|
|
1037
|
-
/// The first argument can be either a string or a tuple of strings.
|
|
1038
|
-
fn parse_prefix_suffix_args(
|
|
1039
|
-
method: &str,
|
|
1040
|
-
s: &str,
|
|
1041
|
-
args: ArgValues,
|
|
1042
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
1043
|
-
) -> RunResult<(Vec<String>, usize, usize)> {
|
|
1044
|
-
let pos = args.into_pos_only(method, vm.heap)?;
|
|
1045
|
-
defer_drop!(pos, vm);
|
|
1046
|
-
|
|
1047
|
-
let str_len = s.chars().count();
|
|
1048
|
-
match pos.as_slice() {
|
|
1049
|
-
[prefix_value] => {
|
|
1050
|
-
let prefixes = extract_str_or_tuple_of_str(prefix_value, vm)?;
|
|
1051
|
-
Ok((prefixes, 0, str_len))
|
|
1052
|
-
}
|
|
1053
|
-
[prefix_value, start_value] => {
|
|
1054
|
-
let prefixes = extract_str_or_tuple_of_str(prefix_value, vm)?;
|
|
1055
|
-
let start = optional_index(start_value, 0, str_len, vm.heap)?;
|
|
1056
|
-
Ok((prefixes, start, str_len))
|
|
1057
|
-
}
|
|
1058
|
-
[prefix_value, start_value, end_value] => {
|
|
1059
|
-
let prefixes = extract_str_or_tuple_of_str(prefix_value, vm)?;
|
|
1060
|
-
let start = optional_index(start_value, 0, str_len, vm.heap)?;
|
|
1061
|
-
let end = optional_index(end_value, str_len, str_len, vm.heap)?;
|
|
1062
|
-
Ok((prefixes, start, end))
|
|
1063
|
-
}
|
|
1064
|
-
[] => Err(ExcType::type_error_at_least(method, 1, 0)),
|
|
1065
|
-
_ => Err(ExcType::type_error_at_most(method, 3, pos.len())),
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
/// Extracts a string or tuple of strings from a Value.
|
|
1070
|
-
///
|
|
1071
|
-
/// Returns a Vec of strings - a single-element Vec if given a string,
|
|
1072
|
-
/// or multiple elements if given a tuple of strings.
|
|
1073
|
-
fn extract_str_or_tuple_of_str(value: &Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Vec<String>> {
|
|
1074
|
-
match value {
|
|
1075
|
-
Value::InternString(id) => Ok(vec![vm.interns.get_str(*id).to_owned()]),
|
|
1076
|
-
Value::Ref(heap_id) => match vm.heap.get(*heap_id) {
|
|
1077
|
-
HeapData::Str(s) => Ok(vec![s.as_str().to_owned()]),
|
|
1078
|
-
HeapData::Tuple(tuple) => {
|
|
1079
|
-
// Inline string extraction to avoid borrow conflict — vm.heap is
|
|
1080
|
-
// already borrowed immutably to access the tuple's items.
|
|
1081
|
-
let items = tuple.as_slice();
|
|
1082
|
-
let mut strings = Vec::with_capacity(items.len());
|
|
1083
|
-
for item in items {
|
|
1084
|
-
match item {
|
|
1085
|
-
Value::InternString(id) => {
|
|
1086
|
-
strings.push(vm.interns.get_str(*id).to_owned());
|
|
1087
|
-
}
|
|
1088
|
-
Value::Ref(hid) => {
|
|
1089
|
-
if let HeapData::Str(s) = vm.heap.get(*hid) {
|
|
1090
|
-
strings.push(s.as_str().to_owned());
|
|
1091
|
-
} else {
|
|
1092
|
-
return Err(ExcType::type_error("expected str or tuple of str"));
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
_ => return Err(ExcType::type_error("expected str or tuple of str")),
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
Ok(strings)
|
|
1099
|
-
}
|
|
1100
|
-
_ => Err(ExcType::type_error("expected str or tuple of str")),
|
|
1101
|
-
},
|
|
1102
|
-
_ => Err(ExcType::type_error("expected str or tuple of str")),
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
/// Extracts a string from a Value, returning an error if not a string.
|
|
1107
|
-
fn extract_string_arg(value: &Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<String> {
|
|
1108
|
-
match value {
|
|
1109
|
-
Value::InternString(id) => Ok(vm.interns.get_str(*id).to_owned()),
|
|
1110
|
-
Value::Ref(heap_id) => {
|
|
1111
|
-
if let HeapData::Str(s) = vm.heap.get(*heap_id) {
|
|
1112
|
-
Ok(s.as_str().to_owned())
|
|
1113
|
-
} else {
|
|
1114
|
-
Err(ExcType::type_error("expected str"))
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
_ => Err(ExcType::type_error("expected str")),
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
/// Extracts an integer from a Value, returning an error if not an integer.
|
|
1122
|
-
fn extract_int_arg(value: &Value, heap: &Heap<impl ResourceTracker>) -> RunResult<i64> {
|
|
1123
|
-
match value {
|
|
1124
|
-
Value::Int(i) => Ok(*i),
|
|
1125
|
-
Value::Ref(heap_id) => {
|
|
1126
|
-
if let HeapData::LongInt(li) = heap.get(*heap_id) {
|
|
1127
|
-
// Try to convert to i64
|
|
1128
|
-
li.to_i64().ok_or_else(|| ExcType::type_error("integer too large"))
|
|
1129
|
-
} else {
|
|
1130
|
-
Err(ExcType::type_error("expected int"))
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
_ => Err(ExcType::type_error("expected int")),
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
/// Normalizes a Python-style index to a valid index in range [0, len].
|
|
1138
|
-
fn normalize_index(index: i64, len: usize) -> usize {
|
|
1139
|
-
if index < 0 {
|
|
1140
|
-
// Safe cast: we've checked index is negative, so -index is positive
|
|
1141
|
-
// For very large negative numbers that don't fit in usize, saturate to usize::MAX
|
|
1142
|
-
let abs_index = usize::try_from(-index).unwrap_or(usize::MAX);
|
|
1143
|
-
len.saturating_sub(abs_index)
|
|
1144
|
-
} else {
|
|
1145
|
-
// Safe cast: we've checked index is non-negative
|
|
1146
|
-
// For values > usize::MAX, saturate to len
|
|
1147
|
-
usize::try_from(index).unwrap_or(len).min(len)
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
/// Extracts an optional index from a `Value`, treating `None` as `default`.
|
|
1152
|
-
///
|
|
1153
|
-
/// Used by argument parsers where `None` means "use the default index" and
|
|
1154
|
-
/// any other value is interpreted as an integer and normalized against `str_len`.
|
|
1155
|
-
fn optional_index(
|
|
1156
|
-
value: &Value,
|
|
1157
|
-
default: usize,
|
|
1158
|
-
str_len: usize,
|
|
1159
|
-
heap: &Heap<impl ResourceTracker>,
|
|
1160
|
-
) -> RunResult<usize> {
|
|
1161
|
-
if matches!(value, Value::None) {
|
|
1162
|
-
Ok(default)
|
|
1163
|
-
} else {
|
|
1164
|
-
Ok(normalize_index(extract_int_arg(value, heap)?, str_len))
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
/// Returns a substring of s from character index start to end.
|
|
1169
|
-
fn slice_string(s: &str, start: usize, end: usize) -> &str {
|
|
1170
|
-
if start >= end {
|
|
1171
|
-
return "";
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
let mut start_byte = s.len();
|
|
1175
|
-
let mut end_byte = s.len();
|
|
1176
|
-
|
|
1177
|
-
for (char_idx, (byte_idx, _)) in s.char_indices().enumerate() {
|
|
1178
|
-
if char_idx == start {
|
|
1179
|
-
start_byte = byte_idx;
|
|
1180
|
-
}
|
|
1181
|
-
if char_idx == end {
|
|
1182
|
-
end_byte = byte_idx;
|
|
1183
|
-
break;
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
&s[start_byte..end_byte]
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// =============================================================================
|
|
1191
|
-
// Strip/trim methods
|
|
1192
|
-
// =============================================================================
|
|
1193
|
-
|
|
1194
|
-
/// Implements Python's `str.strip(chars?)` method.
|
|
1195
|
-
///
|
|
1196
|
-
/// Returns a copy of the string with leading and trailing characters removed.
|
|
1197
|
-
/// If chars is not specified, whitespace characters are removed.
|
|
1198
|
-
fn str_strip(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1199
|
-
let chars = parse_strip_arg("str.strip", args, vm)?;
|
|
1200
|
-
let result = match &chars {
|
|
1201
|
-
Some(c) => s.trim_matches(|ch| c.contains(ch)).to_owned(),
|
|
1202
|
-
None => s.trim().to_owned(),
|
|
1203
|
-
};
|
|
1204
|
-
allocate_string(result, vm.heap)
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
/// Implements Python's `str.lstrip(chars?)` method.
|
|
1208
|
-
///
|
|
1209
|
-
/// Returns a copy of the string with leading characters removed.
|
|
1210
|
-
fn str_lstrip(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1211
|
-
let chars = parse_strip_arg("str.lstrip", args, vm)?;
|
|
1212
|
-
let result = match &chars {
|
|
1213
|
-
Some(c) => s.trim_start_matches(|ch| c.contains(ch)).to_owned(),
|
|
1214
|
-
None => s.trim_start().to_owned(),
|
|
1215
|
-
};
|
|
1216
|
-
allocate_string(result, vm.heap)
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
/// Implements Python's `str.rstrip(chars?)` method.
|
|
1220
|
-
///
|
|
1221
|
-
/// Returns a copy of the string with trailing characters removed.
|
|
1222
|
-
fn str_rstrip(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1223
|
-
let chars = parse_strip_arg("str.rstrip", args, vm)?;
|
|
1224
|
-
let result = match &chars {
|
|
1225
|
-
Some(c) => s.trim_end_matches(|ch| c.contains(ch)).to_owned(),
|
|
1226
|
-
None => s.trim_end().to_owned(),
|
|
1227
|
-
};
|
|
1228
|
-
allocate_string(result, vm.heap)
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
/// Parses the optional chars argument for strip methods.
|
|
1232
|
-
///
|
|
1233
|
-
/// Accepts None as a value meaning "use default whitespace stripping".
|
|
1234
|
-
fn parse_strip_arg(
|
|
1235
|
-
method: &str,
|
|
1236
|
-
args: ArgValues,
|
|
1237
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
1238
|
-
) -> RunResult<Option<String>> {
|
|
1239
|
-
let value = args.get_zero_one_arg(method, vm.heap)?;
|
|
1240
|
-
match value {
|
|
1241
|
-
None => Ok(None),
|
|
1242
|
-
Some(Value::None) => Ok(None), // Explicit None means default whitespace
|
|
1243
|
-
Some(v) => {
|
|
1244
|
-
defer_drop!(v, vm);
|
|
1245
|
-
let result = extract_string_arg(v, vm)?;
|
|
1246
|
-
Ok(Some(result))
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/// Implements Python's `str.removeprefix(prefix)` method.
|
|
1252
|
-
///
|
|
1253
|
-
/// If the string starts with the prefix string, return string[len(prefix):].
|
|
1254
|
-
/// Otherwise, return a copy of the original string.
|
|
1255
|
-
fn str_removeprefix(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1256
|
-
let prefix_value = args.get_one_arg("str.removeprefix", vm.heap)?;
|
|
1257
|
-
defer_drop!(prefix_value, vm);
|
|
1258
|
-
let prefix = extract_string_arg(prefix_value, vm)?;
|
|
1259
|
-
|
|
1260
|
-
let result = s.strip_prefix(&prefix).unwrap_or(s).to_owned();
|
|
1261
|
-
allocate_string(result, vm.heap)
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
/// Implements Python's `str.removesuffix(suffix)` method.
|
|
1265
|
-
///
|
|
1266
|
-
/// If the string ends with the suffix string, return string[:-len(suffix)].
|
|
1267
|
-
/// Otherwise, return a copy of the original string.
|
|
1268
|
-
fn str_removesuffix(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1269
|
-
let suffix_value = args.get_one_arg("str.removesuffix", vm.heap)?;
|
|
1270
|
-
defer_drop!(suffix_value, vm);
|
|
1271
|
-
let suffix = extract_string_arg(suffix_value, vm)?;
|
|
1272
|
-
|
|
1273
|
-
let result = s.strip_suffix(&suffix).unwrap_or(s).to_owned();
|
|
1274
|
-
allocate_string(result, vm.heap)
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
// =============================================================================
|
|
1278
|
-
// Split methods
|
|
1279
|
-
// =============================================================================
|
|
1280
|
-
|
|
1281
|
-
/// Implements Python's `str.split(sep?, maxsplit?)` method.
|
|
1282
|
-
///
|
|
1283
|
-
/// Returns a list of the words in the string, using sep as the delimiter string.
|
|
1284
|
-
fn str_split(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1285
|
-
let (sep, maxsplit) = parse_split_args("str.split", args, vm)?;
|
|
1286
|
-
|
|
1287
|
-
let parts: Vec<&str> = match &sep {
|
|
1288
|
-
Some(sep) => {
|
|
1289
|
-
// Empty separator raises ValueError
|
|
1290
|
-
if sep.is_empty() {
|
|
1291
|
-
return Err(ExcType::value_error_empty_separator());
|
|
1292
|
-
}
|
|
1293
|
-
if maxsplit < 0 {
|
|
1294
|
-
s.split(sep.as_str()).collect()
|
|
1295
|
-
} else {
|
|
1296
|
-
// Safe cast: we've checked maxsplit >= 0
|
|
1297
|
-
let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
|
|
1298
|
-
s.splitn(max.saturating_add(1), sep.as_str()).collect()
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
None => {
|
|
1302
|
-
// Split on whitespace, filtering empty strings
|
|
1303
|
-
if maxsplit < 0 {
|
|
1304
|
-
s.split_whitespace().collect()
|
|
1305
|
-
} else {
|
|
1306
|
-
// Safe cast: we've checked maxsplit >= 0
|
|
1307
|
-
let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
|
|
1308
|
-
split_whitespace_n(s, max)
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
};
|
|
1312
|
-
|
|
1313
|
-
// Convert to list of strings (using interned empty string when applicable)
|
|
1314
|
-
let mut list_items = Vec::with_capacity(parts.len());
|
|
1315
|
-
for part in parts {
|
|
1316
|
-
vm.heap.check_time()?;
|
|
1317
|
-
list_items.push(allocate_string(part.to_owned(), vm.heap)?);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
let list = crate::types::List::new(list_items);
|
|
1321
|
-
let heap_id = vm.heap.allocate(HeapData::List(list))?;
|
|
1322
|
-
Ok(Value::Ref(heap_id))
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
/// Implements Python's `str.rsplit(sep?, maxsplit?)` method.
|
|
1326
|
-
///
|
|
1327
|
-
/// Returns a list of the words in the string, using sep as the delimiter string,
|
|
1328
|
-
/// splitting from the right.
|
|
1329
|
-
fn str_rsplit(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1330
|
-
let (sep, maxsplit) = parse_split_args("str.rsplit", args, vm)?;
|
|
1331
|
-
|
|
1332
|
-
let parts: Vec<&str> = match &sep {
|
|
1333
|
-
Some(sep) => {
|
|
1334
|
-
// Empty separator raises ValueError
|
|
1335
|
-
if sep.is_empty() {
|
|
1336
|
-
return Err(ExcType::value_error_empty_separator());
|
|
1337
|
-
}
|
|
1338
|
-
if maxsplit < 0 {
|
|
1339
|
-
s.rsplit(sep.as_str()).collect::<Vec<_>>().into_iter().rev().collect()
|
|
1340
|
-
} else {
|
|
1341
|
-
// Safe cast: we've checked maxsplit >= 0
|
|
1342
|
-
let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
|
|
1343
|
-
let mut parts: Vec<_> = s.rsplitn(max.saturating_add(1), sep.as_str()).collect();
|
|
1344
|
-
parts.reverse();
|
|
1345
|
-
parts
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
None => {
|
|
1349
|
-
// Split on whitespace from right
|
|
1350
|
-
if maxsplit < 0 {
|
|
1351
|
-
s.split_whitespace().collect()
|
|
1352
|
-
} else {
|
|
1353
|
-
// Safe cast: we've checked maxsplit >= 0
|
|
1354
|
-
let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
|
|
1355
|
-
rsplit_whitespace_n(s, max)
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
};
|
|
1359
|
-
|
|
1360
|
-
// Convert to list of strings (using interned empty string when applicable)
|
|
1361
|
-
let mut list_items = Vec::with_capacity(parts.len());
|
|
1362
|
-
for part in parts {
|
|
1363
|
-
vm.heap.check_time()?;
|
|
1364
|
-
list_items.push(allocate_string(part.to_owned(), vm.heap)?);
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
let list = crate::types::List::new(list_items);
|
|
1368
|
-
let heap_id = vm.heap.allocate(HeapData::List(list))?;
|
|
1369
|
-
Ok(Value::Ref(heap_id))
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
/// Parses arguments for split methods.
|
|
1373
|
-
///
|
|
1374
|
-
/// Supports both positional and keyword arguments for sep and maxsplit.
|
|
1375
|
-
fn parse_split_args(
|
|
1376
|
-
method: &str,
|
|
1377
|
-
args: ArgValues,
|
|
1378
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
1379
|
-
) -> RunResult<(Option<String>, i64)> {
|
|
1380
|
-
let (pos, kwargs) = args.into_parts();
|
|
1381
|
-
let kwargs_iter = kwargs.into_iter();
|
|
1382
|
-
defer_drop_mut!(kwargs_iter, vm);
|
|
1383
|
-
|
|
1384
|
-
let mut pos_iter = pos;
|
|
1385
|
-
let sep_value = pos_iter.next();
|
|
1386
|
-
defer_drop_mut!(sep_value, vm);
|
|
1387
|
-
let maxsplit_value = pos_iter.next();
|
|
1388
|
-
defer_drop_mut!(maxsplit_value, vm);
|
|
1389
|
-
|
|
1390
|
-
// Check no extra positional arguments
|
|
1391
|
-
if pos_iter.len() != 0 {
|
|
1392
|
-
return Err(ExcType::type_error_at_most(method, 2, 3));
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
// Extract positional sep (default None)
|
|
1396
|
-
let mut has_pos_sep = sep_value.is_some();
|
|
1397
|
-
let mut sep = if let Some(v) = sep_value.as_ref() {
|
|
1398
|
-
if matches!(v, Value::None) {
|
|
1399
|
-
None
|
|
1400
|
-
} else {
|
|
1401
|
-
Some(extract_string_arg(v, vm)?)
|
|
1402
|
-
}
|
|
1403
|
-
} else {
|
|
1404
|
-
None
|
|
1405
|
-
};
|
|
1406
|
-
|
|
1407
|
-
// Extract positional maxsplit (default -1)
|
|
1408
|
-
let mut has_pos_maxsplit = maxsplit_value.is_some();
|
|
1409
|
-
let mut maxsplit = if let Some(v) = maxsplit_value.as_ref() {
|
|
1410
|
-
extract_int_arg(v, vm.heap)?
|
|
1411
|
-
} else {
|
|
1412
|
-
-1
|
|
1413
|
-
};
|
|
1414
|
-
|
|
1415
|
-
// Process kwargs
|
|
1416
|
-
for (key, value) in kwargs_iter {
|
|
1417
|
-
defer_drop!(key, vm);
|
|
1418
|
-
defer_drop!(value, vm);
|
|
1419
|
-
|
|
1420
|
-
let Some(keyword_name) = key.as_either_str(vm.heap) else {
|
|
1421
|
-
return Err(ExcType::type_error("keywords must be strings"));
|
|
1422
|
-
};
|
|
1423
|
-
|
|
1424
|
-
let key_str = keyword_name.as_str(vm.interns);
|
|
1425
|
-
match key_str {
|
|
1426
|
-
"sep" => {
|
|
1427
|
-
if has_pos_sep {
|
|
1428
|
-
return Err(ExcType::type_error(format!(
|
|
1429
|
-
"{method}() got multiple values for argument 'sep'"
|
|
1430
|
-
)));
|
|
1431
|
-
}
|
|
1432
|
-
if matches!(value, Value::None) {
|
|
1433
|
-
sep = None;
|
|
1434
|
-
} else {
|
|
1435
|
-
sep = Some(extract_string_arg(value, vm)?);
|
|
1436
|
-
}
|
|
1437
|
-
has_pos_sep = true;
|
|
1438
|
-
}
|
|
1439
|
-
"maxsplit" => {
|
|
1440
|
-
if has_pos_maxsplit {
|
|
1441
|
-
return Err(ExcType::type_error(format!(
|
|
1442
|
-
"{method}() got multiple values for argument 'maxsplit'"
|
|
1443
|
-
)));
|
|
1444
|
-
}
|
|
1445
|
-
maxsplit = extract_int_arg(value, vm.heap)?;
|
|
1446
|
-
has_pos_maxsplit = true;
|
|
1447
|
-
}
|
|
1448
|
-
_ => {
|
|
1449
|
-
return Err(ExcType::type_error(format!(
|
|
1450
|
-
"'{key_str}' is an invalid keyword argument for {method}()"
|
|
1451
|
-
)));
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
Ok((sep, maxsplit))
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
/// Split string on whitespace, returning at most `maxsplit + 1` parts.
|
|
1460
|
-
fn split_whitespace_n(s: &str, maxsplit: usize) -> Vec<&str> {
|
|
1461
|
-
let mut parts = Vec::new();
|
|
1462
|
-
let mut remaining = s.trim_start();
|
|
1463
|
-
let mut count = 0;
|
|
1464
|
-
|
|
1465
|
-
while !remaining.is_empty() && count < maxsplit {
|
|
1466
|
-
if let Some(end) = remaining.find(|c: char| c.is_whitespace()) {
|
|
1467
|
-
parts.push(&remaining[..end]);
|
|
1468
|
-
remaining = remaining[end..].trim_start();
|
|
1469
|
-
count += 1;
|
|
1470
|
-
} else {
|
|
1471
|
-
break;
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
if !remaining.is_empty() {
|
|
1476
|
-
parts.push(remaining);
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
parts
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
/// Split string on whitespace from the right, returning at most `maxsplit + 1` parts.
|
|
1483
|
-
fn rsplit_whitespace_n(s: &str, maxsplit: usize) -> Vec<&str> {
|
|
1484
|
-
let mut parts = Vec::new();
|
|
1485
|
-
let mut remaining = s.trim_end();
|
|
1486
|
-
let mut count = 0;
|
|
1487
|
-
|
|
1488
|
-
while !remaining.is_empty() && count < maxsplit {
|
|
1489
|
-
if let Some(start) = remaining.rfind(|c: char| c.is_whitespace()) {
|
|
1490
|
-
parts.push(&remaining[start + 1..]);
|
|
1491
|
-
remaining = remaining[..start].trim_end();
|
|
1492
|
-
count += 1;
|
|
1493
|
-
} else {
|
|
1494
|
-
break;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
if !remaining.is_empty() {
|
|
1499
|
-
parts.push(remaining);
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
parts.reverse();
|
|
1503
|
-
parts
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
/// Implements Python's `str.splitlines(keepends?)` method.
|
|
1507
|
-
///
|
|
1508
|
-
/// Returns a list of the lines in the string, breaking at line boundaries.
|
|
1509
|
-
/// Accepts keepends as either positional or keyword argument.
|
|
1510
|
-
fn str_splitlines(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1511
|
-
let keepends = parse_splitlines_args(args, vm)?;
|
|
1512
|
-
|
|
1513
|
-
let mut lines = Vec::new();
|
|
1514
|
-
let mut start = 0;
|
|
1515
|
-
let bytes = s.as_bytes();
|
|
1516
|
-
let len = bytes.len();
|
|
1517
|
-
|
|
1518
|
-
while start < len {
|
|
1519
|
-
vm.heap.check_time()?;
|
|
1520
|
-
|
|
1521
|
-
// Find the next line ending
|
|
1522
|
-
let mut end = start;
|
|
1523
|
-
let mut line_end = start;
|
|
1524
|
-
|
|
1525
|
-
while end < len {
|
|
1526
|
-
match bytes[end] {
|
|
1527
|
-
b'\n' => {
|
|
1528
|
-
line_end = end;
|
|
1529
|
-
end += 1;
|
|
1530
|
-
break;
|
|
1531
|
-
}
|
|
1532
|
-
b'\r' => {
|
|
1533
|
-
line_end = end;
|
|
1534
|
-
end += 1;
|
|
1535
|
-
// Check for \r\n
|
|
1536
|
-
if end < len && bytes[end] == b'\n' {
|
|
1537
|
-
end += 1;
|
|
1538
|
-
}
|
|
1539
|
-
break;
|
|
1540
|
-
}
|
|
1541
|
-
_ => {
|
|
1542
|
-
end += 1;
|
|
1543
|
-
line_end = end;
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
let line = if keepends { &s[start..end] } else { &s[start..line_end] };
|
|
1549
|
-
|
|
1550
|
-
lines.push(allocate_string(line.to_owned(), vm.heap)?);
|
|
1551
|
-
|
|
1552
|
-
start = end;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
let list = crate::types::List::new(lines);
|
|
1556
|
-
let heap_id = vm.heap.allocate(HeapData::List(list))?;
|
|
1557
|
-
Ok(Value::Ref(heap_id))
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
/// Parses arguments for splitlines method.
|
|
1561
|
-
///
|
|
1562
|
-
/// Supports both positional and keyword arguments for keepends.
|
|
1563
|
-
fn parse_splitlines_args(args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<bool> {
|
|
1564
|
-
let (pos, kwargs) = args.into_parts();
|
|
1565
|
-
let kwargs_iter = kwargs.into_iter();
|
|
1566
|
-
defer_drop_mut!(kwargs_iter, vm);
|
|
1567
|
-
|
|
1568
|
-
let mut pos_iter = pos;
|
|
1569
|
-
let keepends_value = pos_iter.next();
|
|
1570
|
-
defer_drop_mut!(keepends_value, vm);
|
|
1571
|
-
|
|
1572
|
-
// Check no extra positional arguments
|
|
1573
|
-
if pos_iter.len() != 0 {
|
|
1574
|
-
return Err(ExcType::type_error_at_most("str.splitlines", 1, 2));
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
// Extract positional keepends (default false)
|
|
1578
|
-
let mut has_pos_keepends = keepends_value.is_some();
|
|
1579
|
-
let mut keepends = if let Some(v) = keepends_value.as_ref() {
|
|
1580
|
-
value_is_truthy(v)
|
|
1581
|
-
} else {
|
|
1582
|
-
false
|
|
1583
|
-
};
|
|
1584
|
-
|
|
1585
|
-
// Process kwargs
|
|
1586
|
-
for (key, value) in kwargs_iter {
|
|
1587
|
-
defer_drop!(key, vm);
|
|
1588
|
-
defer_drop!(value, vm);
|
|
1589
|
-
|
|
1590
|
-
let Some(keyword_name) = key.as_either_str(vm.heap) else {
|
|
1591
|
-
return Err(ExcType::type_error("keywords must be strings"));
|
|
1592
|
-
};
|
|
1593
|
-
|
|
1594
|
-
let key_str = keyword_name.as_str(vm.interns);
|
|
1595
|
-
if key_str == "keepends" {
|
|
1596
|
-
if has_pos_keepends {
|
|
1597
|
-
return Err(ExcType::type_error(
|
|
1598
|
-
"str.splitlines() got multiple values for argument 'keepends'",
|
|
1599
|
-
));
|
|
1600
|
-
}
|
|
1601
|
-
keepends = value_is_truthy(value);
|
|
1602
|
-
has_pos_keepends = true;
|
|
1603
|
-
} else {
|
|
1604
|
-
return Err(ExcType::type_error(format!(
|
|
1605
|
-
"'{key_str}' is an invalid keyword argument for str.splitlines()"
|
|
1606
|
-
)));
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
Ok(keepends)
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
|
-
/// Checks if a value is truthy for bool conversion.
|
|
1614
|
-
fn value_is_truthy(v: &Value) -> bool {
|
|
1615
|
-
match v {
|
|
1616
|
-
Value::Bool(b) => *b,
|
|
1617
|
-
Value::Int(i) => *i != 0,
|
|
1618
|
-
Value::None => false,
|
|
1619
|
-
_ => true, // Most other values are truthy
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
/// Implements Python's `str.partition(sep)` method.
|
|
1624
|
-
///
|
|
1625
|
-
/// Splits the string at the first occurrence of sep, and returns a 3-tuple
|
|
1626
|
-
/// containing the part before the separator, the separator itself, and the part after.
|
|
1627
|
-
fn str_partition(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1628
|
-
let sep_value = args.get_one_arg("str.partition", vm.heap)?;
|
|
1629
|
-
defer_drop!(sep_value, vm);
|
|
1630
|
-
let sep = extract_string_arg(sep_value, vm)?;
|
|
1631
|
-
|
|
1632
|
-
if sep.is_empty() {
|
|
1633
|
-
return Err(ExcType::value_error_empty_separator());
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
let (before, sep_found, after) = match s.find(&sep) {
|
|
1637
|
-
Some(pos) => (&s[..pos], &sep[..], &s[pos + sep.len()..]),
|
|
1638
|
-
None => (s, "", ""),
|
|
1639
|
-
};
|
|
1640
|
-
|
|
1641
|
-
let before_val = allocate_string(before.to_owned(), vm.heap)?;
|
|
1642
|
-
let sep_val = allocate_string(sep_found.to_owned(), vm.heap)?;
|
|
1643
|
-
let after_val = allocate_string(after.to_owned(), vm.heap)?;
|
|
1644
|
-
|
|
1645
|
-
Ok(crate::types::allocate_tuple(
|
|
1646
|
-
smallvec![before_val, sep_val, after_val],
|
|
1647
|
-
vm.heap,
|
|
1648
|
-
)?)
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
/// Implements Python's `str.rpartition(sep)` method.
|
|
1652
|
-
///
|
|
1653
|
-
/// Splits the string at the last occurrence of sep, and returns a 3-tuple
|
|
1654
|
-
/// containing the part before the separator, the separator itself, and the part after.
|
|
1655
|
-
fn str_rpartition(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1656
|
-
let sep_value = args.get_one_arg("str.rpartition", vm.heap)?;
|
|
1657
|
-
defer_drop!(sep_value, vm);
|
|
1658
|
-
let sep = extract_string_arg(sep_value, vm)?;
|
|
1659
|
-
|
|
1660
|
-
if sep.is_empty() {
|
|
1661
|
-
return Err(ExcType::value_error_empty_separator());
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
let (before, sep_found, after) = match s.rfind(&sep) {
|
|
1665
|
-
Some(pos) => (&s[..pos], &sep[..], &s[pos + sep.len()..]),
|
|
1666
|
-
None => ("", "", s),
|
|
1667
|
-
};
|
|
1668
|
-
|
|
1669
|
-
let before_val = allocate_string(before.to_owned(), vm.heap)?;
|
|
1670
|
-
let sep_val = allocate_string(sep_found.to_owned(), vm.heap)?;
|
|
1671
|
-
let after_val = allocate_string(after.to_owned(), vm.heap)?;
|
|
1672
|
-
|
|
1673
|
-
Ok(crate::types::allocate_tuple(
|
|
1674
|
-
smallvec![before_val, sep_val, after_val],
|
|
1675
|
-
vm.heap,
|
|
1676
|
-
)?)
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
// =============================================================================
|
|
1680
|
-
// Replace/modify methods
|
|
1681
|
-
// =============================================================================
|
|
1682
|
-
|
|
1683
|
-
/// Implements Python's `str.replace(old, new, count?)` method.
|
|
1684
|
-
///
|
|
1685
|
-
/// Returns a copy with all occurrences of substring old replaced by new.
|
|
1686
|
-
/// If count is given, only the first count occurrences are replaced.
|
|
1687
|
-
fn str_replace(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1688
|
-
let (old, new, count) = parse_replace_args("str.replace", args, vm)?;
|
|
1689
|
-
|
|
1690
|
-
check_replace_size(s.len(), old.len(), new.len(), count, vm.heap.tracker())?;
|
|
1691
|
-
|
|
1692
|
-
let result = if count < 0 {
|
|
1693
|
-
s.replace(&old, &new)
|
|
1694
|
-
} else {
|
|
1695
|
-
// Safe cast: we've checked count >= 0
|
|
1696
|
-
let n = usize::try_from(count).unwrap_or(usize::MAX);
|
|
1697
|
-
s.replacen(&old, &new, n)
|
|
1698
|
-
};
|
|
1699
|
-
|
|
1700
|
-
allocate_string(result, vm.heap)
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
/// Parses arguments for the replace method.
|
|
1704
|
-
///
|
|
1705
|
-
/// Supports both positional and keyword arguments for count (Python 3.13+).
|
|
1706
|
-
fn parse_replace_args(
|
|
1707
|
-
method: &str,
|
|
1708
|
-
args: ArgValues,
|
|
1709
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
1710
|
-
) -> RunResult<(String, String, i64)> {
|
|
1711
|
-
let (pos, kwargs) = args.into_parts();
|
|
1712
|
-
let kwargs_iter = kwargs.into_iter();
|
|
1713
|
-
defer_drop_mut!(kwargs_iter, vm);
|
|
1714
|
-
|
|
1715
|
-
let mut pos_iter = pos;
|
|
1716
|
-
let Some(old_value) = pos_iter.next() else {
|
|
1717
|
-
return Err(ExcType::type_error_at_least(method, 2, 0));
|
|
1718
|
-
};
|
|
1719
|
-
defer_drop!(old_value, vm);
|
|
1720
|
-
|
|
1721
|
-
let Some(new_value) = pos_iter.next() else {
|
|
1722
|
-
return Err(ExcType::type_error_at_least(method, 2, 1));
|
|
1723
|
-
};
|
|
1724
|
-
defer_drop!(new_value, vm);
|
|
1725
|
-
|
|
1726
|
-
let count_value = pos_iter.next();
|
|
1727
|
-
defer_drop_mut!(count_value, vm);
|
|
1728
|
-
|
|
1729
|
-
// Check no extra positional arguments
|
|
1730
|
-
if pos_iter.len() != 0 {
|
|
1731
|
-
return Err(ExcType::type_error_at_most(method, 3, 4));
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
let old = extract_string_arg(old_value, vm)?;
|
|
1735
|
-
let new = extract_string_arg(new_value, vm)?;
|
|
1736
|
-
|
|
1737
|
-
let mut has_pos_count = count_value.is_some();
|
|
1738
|
-
let mut count = if let Some(v) = count_value.as_ref() {
|
|
1739
|
-
extract_int_arg(v, vm.heap)?
|
|
1740
|
-
} else {
|
|
1741
|
-
-1
|
|
1742
|
-
};
|
|
1743
|
-
|
|
1744
|
-
// Process kwargs (Python 3.13+ allows count as keyword)
|
|
1745
|
-
for (key, value) in kwargs_iter {
|
|
1746
|
-
defer_drop!(key, vm);
|
|
1747
|
-
defer_drop!(value, vm);
|
|
1748
|
-
|
|
1749
|
-
let Some(keyword_name) = key.as_either_str(vm.heap) else {
|
|
1750
|
-
return Err(ExcType::type_error("keywords must be strings"));
|
|
1751
|
-
};
|
|
1752
|
-
|
|
1753
|
-
let key_str = keyword_name.as_str(vm.interns);
|
|
1754
|
-
if key_str == "count" {
|
|
1755
|
-
if has_pos_count {
|
|
1756
|
-
return Err(ExcType::type_error(format!(
|
|
1757
|
-
"{method}() got multiple values for argument 'count'"
|
|
1758
|
-
)));
|
|
1759
|
-
}
|
|
1760
|
-
count = extract_int_arg(value, vm.heap)?;
|
|
1761
|
-
has_pos_count = true;
|
|
1762
|
-
} else {
|
|
1763
|
-
return Err(ExcType::type_error(format!(
|
|
1764
|
-
"'{key_str}' is an invalid keyword argument for {method}()"
|
|
1765
|
-
)));
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
Ok((old, new, count))
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
/// Implements Python's `str.center(width, fillchar?)` method.
|
|
1773
|
-
///
|
|
1774
|
-
/// Returns centered in a string of length width. Padding is done using the
|
|
1775
|
-
/// specified fill character (default is a space).
|
|
1776
|
-
fn str_center(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1777
|
-
let (width, fillchar) = parse_justify_args("str.center", args, vm)?;
|
|
1778
|
-
let len = s.chars().count();
|
|
1779
|
-
|
|
1780
|
-
let result = if width <= len {
|
|
1781
|
-
s.to_owned()
|
|
1782
|
-
} else {
|
|
1783
|
-
check_repeat_size(width, fillchar.len_utf8(), vm.heap.tracker())?;
|
|
1784
|
-
let total_pad = width - len;
|
|
1785
|
-
let left_pad = total_pad / 2;
|
|
1786
|
-
let right_pad = total_pad - left_pad;
|
|
1787
|
-
let mut result = String::with_capacity(width);
|
|
1788
|
-
for _ in 0..left_pad {
|
|
1789
|
-
result.push(fillchar);
|
|
1790
|
-
}
|
|
1791
|
-
result.push_str(s);
|
|
1792
|
-
for _ in 0..right_pad {
|
|
1793
|
-
result.push(fillchar);
|
|
1794
|
-
}
|
|
1795
|
-
result
|
|
1796
|
-
};
|
|
1797
|
-
|
|
1798
|
-
allocate_string(result, vm.heap)
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
/// Implements Python's `str.ljust(width, fillchar?)` method.
|
|
1802
|
-
///
|
|
1803
|
-
/// Returns left-justified in a string of length width.
|
|
1804
|
-
fn str_ljust(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1805
|
-
let (width, fillchar) = parse_justify_args("str.ljust", args, vm)?;
|
|
1806
|
-
let len = s.chars().count();
|
|
1807
|
-
|
|
1808
|
-
let result = if width <= len {
|
|
1809
|
-
s.to_owned()
|
|
1810
|
-
} else {
|
|
1811
|
-
check_repeat_size(width, fillchar.len_utf8(), vm.heap.tracker())?;
|
|
1812
|
-
let pad = width - len;
|
|
1813
|
-
let mut result = String::with_capacity(width);
|
|
1814
|
-
result.push_str(s);
|
|
1815
|
-
for _ in 0..pad {
|
|
1816
|
-
result.push(fillchar);
|
|
1817
|
-
}
|
|
1818
|
-
result
|
|
1819
|
-
};
|
|
1820
|
-
|
|
1821
|
-
allocate_string(result, vm.heap)
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
/// Implements Python's `str.rjust(width, fillchar?)` method.
|
|
1825
|
-
///
|
|
1826
|
-
/// Returns right-justified in a string of length width.
|
|
1827
|
-
fn str_rjust(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1828
|
-
let (width, fillchar) = parse_justify_args("str.rjust", args, vm)?;
|
|
1829
|
-
let len = s.chars().count();
|
|
1830
|
-
|
|
1831
|
-
let result = if width <= len {
|
|
1832
|
-
s.to_owned()
|
|
1833
|
-
} else {
|
|
1834
|
-
check_repeat_size(width, fillchar.len_utf8(), vm.heap.tracker())?;
|
|
1835
|
-
let pad = width - len;
|
|
1836
|
-
let mut result = String::with_capacity(width);
|
|
1837
|
-
for _ in 0..pad {
|
|
1838
|
-
result.push(fillchar);
|
|
1839
|
-
}
|
|
1840
|
-
result.push_str(s);
|
|
1841
|
-
result
|
|
1842
|
-
};
|
|
1843
|
-
|
|
1844
|
-
allocate_string(result, vm.heap)
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
/// Parses arguments for justify methods (center, ljust, rjust).
|
|
1848
|
-
fn parse_justify_args(
|
|
1849
|
-
method: &str,
|
|
1850
|
-
args: ArgValues,
|
|
1851
|
-
vm: &mut VM<'_, '_, impl ResourceTracker>,
|
|
1852
|
-
) -> RunResult<(usize, char)> {
|
|
1853
|
-
let pos = args.into_pos_only(method, vm.heap)?;
|
|
1854
|
-
defer_drop!(pos, vm);
|
|
1855
|
-
|
|
1856
|
-
match pos.as_slice() {
|
|
1857
|
-
[width_value] => {
|
|
1858
|
-
let w = extract_int_arg(width_value, vm.heap)?;
|
|
1859
|
-
let width = if w < 0 {
|
|
1860
|
-
0
|
|
1861
|
-
} else {
|
|
1862
|
-
usize::try_from(w).unwrap_or(usize::MAX)
|
|
1863
|
-
};
|
|
1864
|
-
Ok((width, ' '))
|
|
1865
|
-
}
|
|
1866
|
-
[width_value, fillchar_value] => {
|
|
1867
|
-
let w = extract_int_arg(width_value, vm.heap)?;
|
|
1868
|
-
let width = if w < 0 {
|
|
1869
|
-
0
|
|
1870
|
-
} else {
|
|
1871
|
-
usize::try_from(w).unwrap_or(usize::MAX)
|
|
1872
|
-
};
|
|
1873
|
-
let fill_str = extract_string_arg(fillchar_value, vm)?;
|
|
1874
|
-
if fill_str.chars().count() != 1 {
|
|
1875
|
-
return Err(ExcType::type_error_fillchar_must_be_single_char());
|
|
1876
|
-
}
|
|
1877
|
-
Ok((width, fill_str.chars().next().unwrap()))
|
|
1878
|
-
}
|
|
1879
|
-
[] => Err(ExcType::type_error_at_least(method, 1, 0)),
|
|
1880
|
-
_ => Err(ExcType::type_error_at_most(method, 2, pos.len())),
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
/// Implements Python's `str.zfill(width)` method.
|
|
1885
|
-
///
|
|
1886
|
-
/// Returns a copy of the string left filled with ASCII '0' digits to make a
|
|
1887
|
-
/// string of length width. A sign prefix is handled correctly.
|
|
1888
|
-
fn str_zfill(s: &str, args: ArgValues, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
|
|
1889
|
-
let width_value = args.get_one_arg("str.zfill", heap)?;
|
|
1890
|
-
defer_drop!(width_value, heap);
|
|
1891
|
-
let width_i64 = extract_int_arg(width_value, heap)?;
|
|
1892
|
-
|
|
1893
|
-
// Safe cast: treat negative as 0, saturate large positive values
|
|
1894
|
-
let width = if width_i64 < 0 {
|
|
1895
|
-
0
|
|
1896
|
-
} else {
|
|
1897
|
-
usize::try_from(width_i64).unwrap_or(usize::MAX)
|
|
1898
|
-
};
|
|
1899
|
-
let len = s.chars().count();
|
|
1900
|
-
|
|
1901
|
-
let result = if width <= len {
|
|
1902
|
-
s.to_owned()
|
|
1903
|
-
} else {
|
|
1904
|
-
// zfill always pads with ASCII '0' (1 byte)
|
|
1905
|
-
check_repeat_size(width, 1, heap.tracker())?;
|
|
1906
|
-
let pad = width - len;
|
|
1907
|
-
let mut chars = s.chars();
|
|
1908
|
-
let first = chars.next();
|
|
1909
|
-
|
|
1910
|
-
let mut result = String::with_capacity(width);
|
|
1911
|
-
|
|
1912
|
-
// Handle sign prefix
|
|
1913
|
-
if matches!(first, Some('+' | '-')) {
|
|
1914
|
-
result.push(first.unwrap());
|
|
1915
|
-
for _ in 0..pad {
|
|
1916
|
-
result.push('0');
|
|
1917
|
-
}
|
|
1918
|
-
result.extend(chars);
|
|
1919
|
-
} else {
|
|
1920
|
-
for _ in 0..pad {
|
|
1921
|
-
result.push('0');
|
|
1922
|
-
}
|
|
1923
|
-
result.push_str(s);
|
|
1924
|
-
}
|
|
1925
|
-
result
|
|
1926
|
-
};
|
|
1927
|
-
|
|
1928
|
-
allocate_string(result, heap)
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
|
-
/// Implements Python's `str.encode(encoding='utf-8', errors='strict')` method.
|
|
1932
|
-
///
|
|
1933
|
-
/// Returns an encoded version of the string as a bytes object. Only supports
|
|
1934
|
-
/// UTF-8 encoding (the native encoding for Rust strings).
|
|
1935
|
-
fn str_encode(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
|
|
1936
|
-
let (encoding, errors) = parse_encode_args(args, vm)?;
|
|
1937
|
-
|
|
1938
|
-
// Only UTF-8 is supported - Rust strings are always valid UTF-8
|
|
1939
|
-
let encoding_lower = encoding.to_ascii_lowercase();
|
|
1940
|
-
if encoding_lower != "utf-8" && encoding_lower != "utf8" {
|
|
1941
|
-
return Err(ExcType::lookup_error_unknown_encoding(&encoding));
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
// For UTF-8 encoding of a valid UTF-8 string, errors mode doesn't matter
|
|
1945
|
-
// since there's nothing to handle - the string is already valid UTF-8
|
|
1946
|
-
if errors != "strict" && errors != "ignore" && errors != "replace" && errors != "backslashreplace" {
|
|
1947
|
-
return Err(ExcType::lookup_error_unknown_error_handler(&errors));
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
let bytes = s.as_bytes().to_vec();
|
|
1951
|
-
let heap_id = vm.heap.allocate(HeapData::Bytes(Bytes::new(bytes)))?;
|
|
1952
|
-
Ok(Value::Ref(heap_id))
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
/// Parses arguments for `str.encode()`.
|
|
1956
|
-
///
|
|
1957
|
-
/// Returns (encoding, errors) with defaults "utf-8" and "strict".
|
|
1958
|
-
fn parse_encode_args(args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<(String, String)> {
|
|
1959
|
-
let (first, second) = args.get_zero_one_two_args("str.encode", vm.heap)?;
|
|
1960
|
-
|
|
1961
|
-
let encoding = if let Some(v) = first {
|
|
1962
|
-
defer_drop!(v, vm);
|
|
1963
|
-
extract_string_arg(v, vm)?
|
|
1964
|
-
} else {
|
|
1965
|
-
"utf-8".to_owned()
|
|
1966
|
-
};
|
|
1967
|
-
|
|
1968
|
-
let errors = if let Some(v) = second {
|
|
1969
|
-
defer_drop!(v, vm);
|
|
1970
|
-
extract_string_arg(v, vm)?
|
|
1971
|
-
} else {
|
|
1972
|
-
"strict".to_owned()
|
|
1973
|
-
};
|
|
1974
|
-
|
|
1975
|
-
Ok((encoding, errors))
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
/// Implements Python's `str.isidentifier()` predicate.
|
|
1979
|
-
///
|
|
1980
|
-
/// Returns True if the string is a valid Python identifier according to
|
|
1981
|
-
/// the language definition (starts with letter or underscore, followed by
|
|
1982
|
-
/// letters, digits, or underscores). Empty strings return False.
|
|
1983
|
-
fn str_isidentifier(s: &str) -> bool {
|
|
1984
|
-
if s.is_empty() {
|
|
1985
|
-
return false;
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
let mut chars = s.chars();
|
|
1989
|
-
|
|
1990
|
-
// First character must be a letter (Unicode) or underscore
|
|
1991
|
-
let first = chars.next().unwrap();
|
|
1992
|
-
if !is_xid_start(first) && first != '_' {
|
|
1993
|
-
return false;
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
// Remaining characters must be letters, digits (Unicode), or underscores
|
|
1997
|
-
chars.all(is_xid_continue)
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
/// Checks if a character is valid at the start of an identifier (XID_Start).
|
|
2001
|
-
///
|
|
2002
|
-
/// This is a simplified implementation that covers ASCII and common Unicode letters.
|
|
2003
|
-
/// Python uses the full Unicode XID_Start property.
|
|
2004
|
-
fn is_xid_start(c: char) -> bool {
|
|
2005
|
-
c.is_alphabetic()
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
/// Checks if a character is valid in the continuation of an identifier (XID_Continue).
|
|
2009
|
-
///
|
|
2010
|
-
/// This is a simplified implementation that covers ASCII and common Unicode.
|
|
2011
|
-
/// Python uses the full Unicode XID_Continue property.
|
|
2012
|
-
fn is_xid_continue(c: char) -> bool {
|
|
2013
|
-
c.is_alphanumeric() || c == '_'
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
/// Implements Python's `str.istitle()` predicate.
|
|
2017
|
-
///
|
|
2018
|
-
/// Returns True if the string is titlecased: uppercase characters follow
|
|
2019
|
-
/// uncased characters and lowercase characters follow cased characters.
|
|
2020
|
-
/// Empty strings return False.
|
|
2021
|
-
fn str_istitle(s: &str) -> bool {
|
|
2022
|
-
if s.is_empty() {
|
|
2023
|
-
return false;
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
let mut prev_cased = false;
|
|
2027
|
-
let mut has_cased = false;
|
|
2028
|
-
|
|
2029
|
-
for c in s.chars() {
|
|
2030
|
-
if c.is_uppercase() {
|
|
2031
|
-
// Uppercase must follow uncased
|
|
2032
|
-
if prev_cased {
|
|
2033
|
-
return false;
|
|
2034
|
-
}
|
|
2035
|
-
prev_cased = true;
|
|
2036
|
-
has_cased = true;
|
|
2037
|
-
} else if c.is_lowercase() {
|
|
2038
|
-
// Lowercase must follow cased
|
|
2039
|
-
if !prev_cased {
|
|
2040
|
-
return false;
|
|
2041
|
-
}
|
|
2042
|
-
prev_cased = true;
|
|
2043
|
-
has_cased = true;
|
|
2044
|
-
} else {
|
|
2045
|
-
// Uncased character
|
|
2046
|
-
prev_cased = false;
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
|
-
has_cased
|
|
2051
|
-
}
|