[{"data":1,"prerenderedAt":2949},["ShallowReactive",2],{"navigation":3,"/rust/production-cli-workflow":458,"/rust/production-cli-workflow-surround":2944},[4,58,91,178,194,201,249,295,315,371,435,449],{"title":5,"path":6,"stem":7,"children":8,"icon":57},"MacOS","/macos","01.macos/01.index",[9,12,17,22,27,32,37,42,47,52],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-house",{"title":13,"path":14,"stem":15,"icon":16},"Recommended Software","/macos/recommended-software","01.macos/02.recommended-software","i-lucide-laptop",{"title":18,"path":19,"stem":20,"icon":21},"Development Applications and Tools","/macos/development-tools","01.macos/03.development-tools","i-lucide-folder-code",{"title":23,"path":24,"stem":25,"icon":26},"Command Line Interface","/macos/cli-tools","01.macos/04.cli-tools","i-lucide-terminal",{"title":28,"path":29,"stem":30,"icon":31},"ZSH Configuration","/macos/zshrc","01.macos/05.zshrc","i-simple-icons-zsh",{"title":33,"path":34,"stem":35,"icon":36},"Uninstall Python","/macos/uninstall-python","01.macos/06.uninstall-python","i-simple-icons-python",{"title":38,"path":39,"stem":40,"icon":41},"Mac Mini Server Setup","/macos/mac-mini-server","01.macos/07.mac-mini-server","i-lucide-server",{"title":43,"path":44,"stem":45,"icon":46},"Image to 3D Model","/macos/image-to-3d","01.macos/08.image-to-3d","i-lucide-rotate-3d",{"title":48,"path":49,"stem":50,"icon":51},"NSVisualEffectView","/macos/material-view","01.macos/09.material-view","i-lucide-layers",{"title":53,"path":54,"stem":55,"icon":56},"SSH","/macos/ssh","01.macos/10.ssh","i-lucide-key-round","i-simple-icons-macos",{"title":59,"path":60,"stem":61,"children":62,"icon":90},"Windows","/windows","02.windows/01.index",[63,64,67,70,75,80,85],{"title":10,"path":60,"stem":61,"icon":11},{"title":13,"path":65,"stem":66,"icon":21},"/windows/recommended-software","02.windows/02.recommended-software",{"title":23,"path":68,"stem":69,"icon":26},"/windows/cli-tools","02.windows/03.cli-tools",{"title":71,"path":72,"stem":73,"icon":74},"Miscellaneous Tips","/windows/miscellaneous-tips","02.windows/04.miscellaneous-tips","i-lucide-lightbulb",{"title":76,"path":77,"stem":78,"icon":79},"Keyboard Shortcuts","/windows/keyboard-shortcuts","02.windows/05.keyboard-shortcuts","i-lucide-keyboard",{"title":81,"path":82,"stem":83,"icon":84},"WSL - Arch Linux","/windows/wsl-arch-linux","02.windows/06.wsl-arch-linux","i-simple-icons-archlinux",{"title":86,"path":87,"stem":88,"icon":89},"VirtualBox on Windows","/windows/virtualbox","02.windows/07.virtualbox","i-simple-icons-virtualbox","i-simple-icons-windows",{"title":92,"path":93,"stem":94,"children":95,"icon":177},"Rust","/rust","03.rust/01.index",[96,98,103,108,113,118,123,128,132,136,140,144,148,152,156,161,165,169,173],{"title":97,"path":93,"stem":94,"icon":26},"Installation",{"title":99,"path":100,"stem":101,"icon":102},"Continuous Integration","/rust/ci","03.rust/02.ci","i-lucide-workflow",{"title":104,"path":105,"stem":106,"icon":107},"Benchmarking with Criterion","/rust/benchmarking","03.rust/03.benchmarking","i-lucide-timer",{"title":109,"path":110,"stem":111,"icon":112},"Publishing Crates","/rust/publishing-crates","03.rust/04.publishing-crates","i-lucide-package",{"title":114,"path":115,"stem":116,"icon":117},"Multi-Crate Dependency Management","/rust/multi-crate-dependencies","03.rust/05.multi-crate-dependencies","i-lucide-git-branch",{"title":119,"path":120,"stem":121,"icon":122},"Just Task Runner","/rust/just-task-runner","03.rust/06.just-task-runner","i-lucide-play",{"title":124,"path":125,"stem":126,"icon":127},"Error Handling Patterns","/rust/error-handling","03.rust/07.error-handling","i-lucide-alert-triangle",{"title":129,"path":130,"stem":131},"Mutation Testing","/rust/mutation-testing","03.rust/08.mutation-testing",{"title":133,"path":134,"stem":135},"Semver Compliance with cargo-semver-checks","/rust/semver-checks","03.rust/09.semver-checks",{"title":137,"path":138,"stem":139},"Property-Based Testing","/rust/property-testing","03.rust/10.property-testing",{"title":141,"path":142,"stem":143},"Workspace Automation","/rust/workspace-automation","03.rust/11.workspace-automation",{"title":145,"path":146,"stem":147},"Type-Safe Units","/rust/type-safe-units","03.rust/12.type-safe-units",{"title":149,"path":150,"stem":151},"Faster Testing with nextest","/rust/nextest","03.rust/13.nextest",{"title":153,"path":154,"stem":155},"Cross-Crate Validation Testing","/rust/cross-crate-validation","03.rust/14.cross-crate-validation",{"title":157,"path":158,"stem":159,"icon":160},"Defensive API Design","/rust/defensive-api-design","03.rust/15.defensive-api-design","i-lucide-shield",{"title":162,"path":163,"stem":164},"Structured Logging with tracing","/rust/tracing","03.rust/16.tracing",{"title":166,"path":167,"stem":168},"Feature Flags & Conditional Compilation","/rust/feature-flags","03.rust/17.feature-flags",{"title":170,"path":171,"stem":172},"Debugging Rust","/rust/debugging","03.rust/18.debugging",{"title":174,"path":175,"stem":176},"Production-Ready Rust CLI Workflow","/rust/production-cli-workflow","03.rust/18.production-cli-workflow","i-simple-icons-rust",{"title":179,"path":180,"stem":181,"children":182,"icon":36},"Python","/python","04.python/01.index",[183,184,189],{"title":97,"path":180,"stem":181,"icon":26},{"title":185,"path":186,"stem":187,"icon":188},"Optionally Callable Decorators","/python/decorators","04.python/02.decorators","i-lucide-at-sign",{"title":190,"path":191,"stem":192,"icon":193},"Pre-commit, with uv","/python/pre-commit","04.python/03.pre-commit","i-simple-icons-precommit",{"title":195,"path":196,"stem":197,"children":198,"icon":200},"Go","/go","05.go/01.index",[199],{"title":97,"path":196,"stem":197,"icon":26},"i-simple-icons-go",{"title":202,"path":203,"stem":204,"children":205,"icon":248},"Web Design","/webdesign","06.webdesign/01.index",[206,209,214,219,224,229,234,239,244],{"title":207,"path":203,"stem":204,"icon":208},"Icons","i-simple-icons-lucide",{"title":210,"path":211,"stem":212,"icon":213},"CSS","/webdesign/css","06.webdesign/02.css","i-simple-icons-css",{"title":215,"path":216,"stem":217,"icon":218},"Captive Portals","/webdesign/captive-portals","06.webdesign/03.captive-portals","i-lucide-wifi",{"title":220,"path":221,"stem":222,"icon":223},"URL Pattern API","/webdesign/url-pattern","06.webdesign/04.url-pattern","i-lucide-link",{"title":225,"path":226,"stem":227,"icon":228},"Hidden Until Found","/webdesign/hidden-until-found","06.webdesign/05.hidden-until-found","i-lucide-search",{"title":230,"path":231,"stem":232,"icon":233},"Login Patterns","/webdesign/login-patterns","06.webdesign/06.login-patterns","i-lucide-log-in",{"title":235,"path":236,"stem":237,"icon":238},"Typography","/webdesign/typography","06.webdesign/07.typography","i-lucide-type",{"title":240,"path":241,"stem":242,"icon":243},"UI Details","/webdesign/ui-details","06.webdesign/08.ui-details","i-lucide-sparkles",{"title":245,"path":246,"stem":247},"Draftboard","/webdesign/draftboard","06.webdesign/09.draftboard","i-lucide-arrow-down-up",{"title":250,"icon":251,"path":252,"stem":253,"children":254,"page":294},"Health","i-lucide-heart-pulse","/health","07.health",[255,260,265,270,275,280,285,290],{"title":256,"path":257,"stem":258,"icon":259},"ADHD and Sleep Cycles","/health/adhd-sleep","07.health/02.adhd-sleep","i-lucide-moon",{"title":261,"path":262,"stem":263,"icon":264},"Daily Health Habits","/health/daily-health-habits","07.health/03.daily-health-habits","i-lucide-list-checks",{"title":266,"path":267,"stem":268,"icon":269},"Calm Down Posture","/health/calm-down-posture","07.health/04.calm-down-posture","i-lucide-bed",{"title":271,"path":272,"stem":273,"icon":274},"Mood Switch on Your Face","/health/mood-face","07.health/05.mood-face","i-lucide-smile",{"title":276,"path":277,"stem":278,"icon":279},"Baby Gas Relief","/health/baby-gas-relief","07.health/06.baby-gas-relief","i-lucide-baby",{"title":281,"path":282,"stem":283,"icon":284},"Fast Acting Wound Sealer","/health/wound-sealer","07.health/07.wound-sealer","i-lucide-bandage",{"title":286,"path":287,"stem":288,"icon":289},"Water Flosser","/health/waterpick","07.health/08.waterpick","i-lucide-droplets",{"title":291,"path":292,"stem":293},"Meal Timing & Sleep","/health/meal-timing","07.health/09.meal-timing",false,{"title":296,"icon":297,"path":298,"stem":299,"children":300,"page":294},"Strategy","i-lucide-target","/strategy","08.strategy",[301,305,310],{"title":302,"path":303,"stem":304,"icon":297},"Jobs to Be Done","/strategy/jobs-to-be-done","08.strategy/02.jobs-to-be-done",{"title":306,"path":307,"stem":308,"icon":309},"Pillars of Money","/strategy/pillars-of-money","08.strategy/03.pillars-of-money","i-lucide-landmark",{"title":311,"path":312,"stem":313,"icon":314},"Marketing","/strategy/marketing","08.strategy/04.marketing","i-lucide-megaphone",{"title":316,"icon":317,"path":318,"stem":319,"children":320,"page":294},"Artificial Intelligence","i-lucide-brain","/ai","09.ai",[321,325,330,334,338,342,347,352,357,362,366],{"title":322,"path":323,"stem":324},"Linear + Cursor + Codex Workflow Integration","/ai/workflow-integration","09.ai/01.workflow-integration",{"title":326,"path":327,"stem":328,"icon":329},"Fabric","/ai/fabric","09.ai/02.fabric","i-lucide-cpu",{"title":331,"path":332,"stem":333},"OpenCode: Open Source AI Coding Agent","/ai/opencode-agent","09.ai/02.opencode-agent",{"title":335,"path":336,"stem":337},"Kimi K2.5 + Fireworks AI + OpenCode: Cost-Effective Daily Driver","/ai/kimi-k25-fireworks-opencode","09.ai/03.kimi-k25-fireworks-opencode",{"title":339,"path":340,"stem":341,"icon":259},"Looping Agents at Night","/ai/looping-agents","09.ai/03.looping-agents",{"title":343,"path":344,"stem":345,"icon":346},"Real ML Systems","/ai/real-ml-systems","09.ai/04.real-ml-systems","i-lucide-factory",{"title":348,"path":349,"stem":350,"icon":351},"AI Ralph","/ai/ai-ralph","09.ai/05.ai-ralph","i-lucide-bot",{"title":353,"path":354,"stem":355,"icon":356},"ChatGPT Prompt Techniques","/ai/chatgpt-prompts","09.ai/06.chatgpt-prompts","i-lucide-message-square-text",{"title":358,"path":359,"stem":360,"icon":361},"AI Courses","/ai/ai-courses","09.ai/07.ai-courses","i-lucide-graduation-cap",{"title":363,"path":364,"stem":365,"icon":208},"Claude & Codex Skills","/ai/claude-codex-skills","09.ai/08.claude-codex-skills",{"title":367,"path":368,"stem":369,"icon":370},"OpenDataLoader PDF","/ai/opendataloader-pdf","09.ai/09.opendataloader-pdf","i-lucide-file-text",{"title":372,"icon":329,"path":373,"stem":374,"children":375,"page":294},"Engineering","/engineering","10.engineering",[376,381,386,391,396,401,406,411,416,421,426,431],{"title":377,"path":378,"stem":379,"icon":380},"Aircraft Wing Design","/engineering/aircraft-wing-design","10.engineering/02.aircraft-wing-design","i-lucide-plane",{"title":382,"path":383,"stem":384,"icon":385},"Machinery's Handbook","/engineering/machinery-handbook","10.engineering/03.machinery-handbook","i-lucide-wrench",{"title":387,"path":388,"stem":389,"icon":390},"KiCAD Circuit Snips","/engineering/kicad-circuit-snips","10.engineering/04.kicad-circuit-snips","i-lucide-copy",{"title":392,"path":393,"stem":394,"icon":395},"Enamel Wire for Rework","/engineering/enamel-wire-rework","10.engineering/05.enamel-wire-rework","i-lucide-cable",{"title":397,"path":398,"stem":399,"icon":400},"Treating Steel","/engineering/treating-steel","10.engineering/06.treating-steel","i-lucide-flame",{"title":402,"path":403,"stem":404,"icon":405},"Knipex vs Lindstrom","/engineering/knipex-vs-lindstrom","10.engineering/07.knipex-vs-lindstrom","i-lucide-scissors",{"title":407,"path":408,"stem":409,"icon":410},"Gel-Pak Tack Levels","/engineering/gel-pack-tack","10.engineering/08.gel-pack-tack","i-lucide-grip",{"title":412,"path":413,"stem":414,"icon":415},"Antenna Radiation Patterns","/engineering/antenna-patterns","10.engineering/09.antenna-patterns","i-lucide-radio",{"title":417,"path":418,"stem":419,"icon":420},"Codesign Hardware and Software","/engineering/codesign-hw-sw","10.engineering/10.codesign-hw-sw","i-lucide-circuit-board",{"title":422,"path":423,"stem":424,"icon":425},"Plasticity CAD","/engineering/plasticity-cad","10.engineering/11.plasticity-cad","i-lucide-box",{"title":427,"path":428,"stem":429,"icon":430},"Mission Assurance","/engineering/mission-assurance","10.engineering/12.mission-assurance","i-lucide-shield-check",{"title":432,"path":433,"stem":434},"Google Search Operators","/engineering/google-search-operators","10.engineering/13.google-search-operators",{"title":436,"icon":437,"path":438,"stem":439,"children":440,"page":294},"Learning","i-lucide-book-open","/learning","11.learning",[441,445],{"title":442,"path":443,"stem":444,"icon":74},"Art of Problem Solving","/learning/art-of-problem-solving","11.learning/02.art-of-problem-solving",{"title":446,"path":447,"stem":448,"icon":400},"Do the Work","/learning/do-the-work","11.learning/03.do-the-work",{"title":450,"icon":26,"path":451,"stem":452,"children":453,"page":294},"Node Tools & CLI","/node-tools","12.node-tools",[454],{"title":455,"path":456,"stem":457},"OpenCode: AI Coding Agent for Terminal & CLI","/node-tools/opencode-cli","12.node-tools/01.opencode-cli",{"id":459,"title":174,"body":460,"description":2938,"extension":2939,"links":2940,"meta":2941,"navigation":707,"path":175,"seo":2942,"stem":176,"__hash__":2943},"docs/03.rust/18.production-cli-workflow.md",{"type":461,"value":462,"toc":2918},"minimark",[463,467,471,476,479,509,512,516,534,537,551,601,605,632,635,948,954,957,1444,1448,1455,1676,2070,2074,2284,2287,2328,2332,2335,2647,2650,2690,2708,2712,2721,2733,2737,2743,2747,2760,2764,2767,2771,2783,2787,2790,2847,2850,2881,2885,2888,2911,2914],[464,465,174],"h1",{"id":466},"production-ready-rust-cli-workflow",[468,469,470],"p",{},"This guide walks through a practical pattern for building Rust CLIs you can confidently ship and maintain.",[472,473,475],"h2",{"id":474},"overview","Overview",[468,477,478],{},"You will build a small command-line app that:",[480,481,482,490,500,506],"ul",{},[483,484,485,486],"li",{},"parses arguments with ",[487,488,489],"code",{},"clap",[483,491,492,493,496,497],{},"handles errors cleanly with ",[487,494,495],{},"anyhow"," and ",[487,498,499],{},"thiserror",[483,501,502,503],{},"uses structured logging with ",[487,504,505],{},"tracing",[483,507,508],{},"validates behavior with tests and a release checklist",[468,510,511],{},"Use this as a template for internal tooling, automation scripts, or public developer utilities.",[472,513,515],{"id":514},"prerequisites","Prerequisites",[480,517,518,525,531],{},[483,519,520,521,524],{},"Rust stable toolchain (",[487,522,523],{},"rustup update",")",[483,526,527,528,524],{},"Basic Rust familiarity (modules, structs, ",[487,529,530],{},"Result",[483,532,533],{},"Git initialized for your project",[468,535,536],{},"Optional but recommended:",[480,538,539,545],{},[483,540,541,544],{},[487,542,543],{},"just"," for command recipes",[483,546,547,550],{},[487,548,549],{},"cargo-nextest"," for faster test runs",[552,553,559],"pre",{"className":554,"code":555,"filename":556,"language":557,"meta":558,"style":558},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","rustup update\ncargo install just --locked\ncargo install cargo-nextest --locked\n","Terminal","bash","",[487,560,561,574,589],{"__ignoreMap":558},[562,563,566,570],"span",{"class":564,"line":565},"line",1,[562,567,569],{"class":568},"sBMFI","rustup",[562,571,573],{"class":572},"sfazB"," update\n",[562,575,577,580,583,586],{"class":564,"line":576},2,[562,578,579],{"class":568},"cargo",[562,581,582],{"class":572}," install",[562,584,585],{"class":572}," just",[562,587,588],{"class":572}," --locked\n",[562,590,592,594,596,599],{"class":564,"line":591},3,[562,593,579],{"class":568},[562,595,582],{"class":572},[562,597,598],{"class":572}," cargo-nextest",[562,600,588],{"class":572},[472,602,604],{"id":603},"step-1-create-the-project-scaffold","Step 1: Create the project scaffold",[552,606,608],{"className":554,"code":607,"filename":556,"language":557,"meta":558,"style":558},"cargo new devtool --bin\ncd devtool\n",[487,609,610,623],{"__ignoreMap":558},[562,611,612,614,617,620],{"class":564,"line":565},[562,613,579],{"class":568},[562,615,616],{"class":572}," new",[562,618,619],{"class":572}," devtool",[562,621,622],{"class":572}," --bin\n",[562,624,625,629],{"class":564,"line":576},[562,626,628],{"class":627},"s2Zo4","cd",[562,630,631],{"class":572}," devtool\n",[468,633,634],{},"Add dependencies:",[552,636,641],{"className":637,"code":638,"filename":639,"language":640,"meta":558,"style":558},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[package]\nname = \"devtool\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\nanyhow = \"1\"\nclap = { version = \"4\", features = [\"derive\"] }\nthiserror = \"2\"\ntracing = \"0.1\"\ntracing-subscriber = { version = \"0.3\", features = [\"env-filter\"] }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\n\n[dev-dependencies]\nassert_cmd = \"2\"\npredicates = \"3\"\n","Cargo.toml","toml",[487,642,643,655,673,687,702,709,719,734,782,797,812,852,890,904,909,919,933],{"__ignoreMap":558},[562,644,645,649,652],{"class":564,"line":565},[562,646,648],{"class":647},"sMK4o","[",[562,650,651],{"class":568},"package",[562,653,654],{"class":647},"]\n",[562,656,657,661,664,667,670],{"class":564,"line":576},[562,658,660],{"class":659},"sTEyZ","name ",[562,662,663],{"class":647},"=",[562,665,666],{"class":647}," \"",[562,668,669],{"class":572},"devtool",[562,671,672],{"class":647},"\"\n",[562,674,675,678,680,682,685],{"class":564,"line":591},[562,676,677],{"class":659},"version ",[562,679,663],{"class":647},[562,681,666],{"class":647},[562,683,684],{"class":572},"0.1.0",[562,686,672],{"class":647},[562,688,690,693,695,697,700],{"class":564,"line":689},4,[562,691,692],{"class":659},"edition ",[562,694,663],{"class":647},[562,696,666],{"class":647},[562,698,699],{"class":572},"2024",[562,701,672],{"class":647},[562,703,705],{"class":564,"line":704},5,[562,706,708],{"emptyLinePlaceholder":707},true,"\n",[562,710,712,714,717],{"class":564,"line":711},6,[562,713,648],{"class":647},[562,715,716],{"class":568},"dependencies",[562,718,654],{"class":647},[562,720,722,725,727,729,732],{"class":564,"line":721},7,[562,723,724],{"class":659},"anyhow ",[562,726,663],{"class":647},[562,728,666],{"class":647},[562,730,731],{"class":572},"1",[562,733,672],{"class":647},[562,735,737,740,742,745,748,750,752,755,758,761,764,766,769,771,774,776,779],{"class":564,"line":736},8,[562,738,739],{"class":659},"clap ",[562,741,663],{"class":647},[562,743,744],{"class":647}," {",[562,746,747],{"class":659}," version ",[562,749,663],{"class":647},[562,751,666],{"class":647},[562,753,754],{"class":572},"4",[562,756,757],{"class":647},"\"",[562,759,760],{"class":647},",",[562,762,763],{"class":659}," features ",[562,765,663],{"class":647},[562,767,768],{"class":647}," [",[562,770,757],{"class":647},[562,772,773],{"class":572},"derive",[562,775,757],{"class":647},[562,777,778],{"class":647},"]",[562,780,781],{"class":647}," }\n",[562,783,785,788,790,792,795],{"class":564,"line":784},9,[562,786,787],{"class":659},"thiserror ",[562,789,663],{"class":647},[562,791,666],{"class":647},[562,793,794],{"class":572},"2",[562,796,672],{"class":647},[562,798,800,803,805,807,810],{"class":564,"line":799},10,[562,801,802],{"class":659},"tracing ",[562,804,663],{"class":647},[562,806,666],{"class":647},[562,808,809],{"class":572},"0.1",[562,811,672],{"class":647},[562,813,815,818,820,822,824,826,828,831,833,835,837,839,841,843,846,848,850],{"class":564,"line":814},11,[562,816,817],{"class":659},"tracing-subscriber ",[562,819,663],{"class":647},[562,821,744],{"class":647},[562,823,747],{"class":659},[562,825,663],{"class":647},[562,827,666],{"class":647},[562,829,830],{"class":572},"0.3",[562,832,757],{"class":647},[562,834,760],{"class":647},[562,836,763],{"class":659},[562,838,663],{"class":647},[562,840,768],{"class":647},[562,842,757],{"class":647},[562,844,845],{"class":572},"env-filter",[562,847,757],{"class":647},[562,849,778],{"class":647},[562,851,781],{"class":647},[562,853,855,858,860,862,864,866,868,870,872,874,876,878,880,882,884,886,888],{"class":564,"line":854},12,[562,856,857],{"class":659},"serde ",[562,859,663],{"class":647},[562,861,744],{"class":647},[562,863,747],{"class":659},[562,865,663],{"class":647},[562,867,666],{"class":647},[562,869,731],{"class":572},[562,871,757],{"class":647},[562,873,760],{"class":647},[562,875,763],{"class":659},[562,877,663],{"class":647},[562,879,768],{"class":647},[562,881,757],{"class":647},[562,883,773],{"class":572},[562,885,757],{"class":647},[562,887,778],{"class":647},[562,889,781],{"class":647},[562,891,893,896,898,900,902],{"class":564,"line":892},13,[562,894,895],{"class":659},"serde_json ",[562,897,663],{"class":647},[562,899,666],{"class":647},[562,901,731],{"class":572},[562,903,672],{"class":647},[562,905,907],{"class":564,"line":906},14,[562,908,708],{"emptyLinePlaceholder":707},[562,910,912,914,917],{"class":564,"line":911},15,[562,913,648],{"class":647},[562,915,916],{"class":568},"dev-dependencies",[562,918,654],{"class":647},[562,920,922,925,927,929,931],{"class":564,"line":921},16,[562,923,924],{"class":659},"assert_cmd ",[562,926,663],{"class":647},[562,928,666],{"class":647},[562,930,794],{"class":572},[562,932,672],{"class":647},[562,934,936,939,941,943,946],{"class":564,"line":935},17,[562,937,938],{"class":659},"predicates ",[562,940,663],{"class":647},[562,942,666],{"class":647},[562,944,945],{"class":572},"3",[562,947,672],{"class":647},[472,949,951,952],{"id":950},"step-2-define-cli-shape-with-clap","Step 2: Define CLI shape with ",[487,953,489],{},[468,955,956],{},"Start with a command structure that can grow without breaking UX.",[552,958,963],{"className":959,"code":960,"filename":961,"language":962,"meta":558,"style":558},"language-rust shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","use anyhow::Result;\nuse clap::{Parser, Subcommand};\n\n#[derive(Parser, Debug)]\n#[command(name = \"devtool\")]\n#[command(about = \"Developer automation helper\", long_about = None)]\nstruct Cli {\n    /// Verbose logging (repeat for more detail)\n    #[arg(short, long, action = clap::ArgAction::Count)]\n    verbose: u8,\n\n    #[command(subcommand)]\n    command: Commands,\n}\n\n#[derive(Subcommand, Debug)]\nenum Commands {\n    /// Validate a JSON file\n    Validate {\n        /// Path to JSON file\n        #[arg(value_name = \"FILE\")]\n        file: std::path::PathBuf,\n    },\n}\n\nfn main() -> Result\u003C()> {\n    let cli = Cli::parse();\n    init_tracing(cli.verbose);\n\n    match cli.command {\n        Commands::Validate { file } => validate_file(&file)?,\n    }\n\n    Ok(())\n}\n","src/main.rs","rust",[487,964,965,982,1003,1007,1027,1048,1080,1092,1098,1137,1151,1155,1168,1180,1185,1189,1206,1215,1221,1229,1235,1259,1282,1288,1293,1298,1321,1342,1362,1367,1385,1419,1425,1430,1439],{"__ignoreMap":558},[562,966,967,971,974,977,979],{"class":564,"line":565},[562,968,970],{"class":969},"sbssI","use",[562,972,973],{"class":568}," anyhow",[562,975,976],{"class":647},"::",[562,978,530],{"class":568},[562,980,981],{"class":647},";\n",[562,983,984,986,989,992,995,997,1000],{"class":564,"line":576},[562,985,970],{"class":969},[562,987,988],{"class":568}," clap",[562,990,991],{"class":647},"::{",[562,993,994],{"class":568},"Parser",[562,996,760],{"class":647},[562,998,999],{"class":568}," Subcommand",[562,1001,1002],{"class":647},"};\n",[562,1004,1005],{"class":564,"line":591},[562,1006,708],{"emptyLinePlaceholder":707},[562,1008,1009,1012,1014,1017,1019,1021,1024],{"class":564,"line":689},[562,1010,1011],{"class":647},"#[",[562,1013,773],{"class":659},[562,1015,1016],{"class":647},"(",[562,1018,994],{"class":568},[562,1020,760],{"class":647},[562,1022,1023],{"class":568}," Debug",[562,1025,1026],{"class":647},")]\n",[562,1028,1029,1031,1034,1036,1038,1040,1042,1044,1046],{"class":564,"line":704},[562,1030,1011],{"class":647},[562,1032,1033],{"class":659},"command",[562,1035,1016],{"class":647},[562,1037,660],{"class":659},[562,1039,663],{"class":647},[562,1041,666],{"class":647},[562,1043,669],{"class":572},[562,1045,757],{"class":647},[562,1047,1026],{"class":647},[562,1049,1050,1052,1054,1056,1059,1061,1063,1066,1068,1070,1073,1075,1078],{"class":564,"line":711},[562,1051,1011],{"class":647},[562,1053,1033],{"class":659},[562,1055,1016],{"class":647},[562,1057,1058],{"class":659},"about ",[562,1060,663],{"class":647},[562,1062,666],{"class":647},[562,1064,1065],{"class":572},"Developer automation helper",[562,1067,757],{"class":647},[562,1069,760],{"class":647},[562,1071,1072],{"class":659}," long_about ",[562,1074,663],{"class":647},[562,1076,1077],{"class":568}," None",[562,1079,1026],{"class":647},[562,1081,1082,1086,1089],{"class":564,"line":721},[562,1083,1085],{"class":1084},"spNyl","struct",[562,1087,1088],{"class":568}," Cli",[562,1090,1091],{"class":647}," {\n",[562,1093,1094],{"class":564,"line":736},[562,1095,1097],{"class":1096},"sHwdD","    /// Verbose logging (repeat for more detail)\n",[562,1099,1100,1103,1106,1108,1111,1113,1116,1118,1121,1123,1125,1127,1130,1132,1135],{"class":564,"line":784},[562,1101,1102],{"class":647},"    #[",[562,1104,1105],{"class":659},"arg",[562,1107,1016],{"class":647},[562,1109,1110],{"class":659},"short",[562,1112,760],{"class":647},[562,1114,1115],{"class":659}," long",[562,1117,760],{"class":647},[562,1119,1120],{"class":659}," action ",[562,1122,663],{"class":647},[562,1124,988],{"class":659},[562,1126,976],{"class":647},[562,1128,1129],{"class":568},"ArgAction",[562,1131,976],{"class":647},[562,1133,1134],{"class":568},"Count",[562,1136,1026],{"class":647},[562,1138,1139,1142,1145,1148],{"class":564,"line":799},[562,1140,1141],{"class":659},"    verbose",[562,1143,1144],{"class":647},":",[562,1146,1147],{"class":568}," u8",[562,1149,1150],{"class":647},",\n",[562,1152,1153],{"class":564,"line":814},[562,1154,708],{"emptyLinePlaceholder":707},[562,1156,1157,1159,1161,1163,1166],{"class":564,"line":854},[562,1158,1102],{"class":647},[562,1160,1033],{"class":659},[562,1162,1016],{"class":647},[562,1164,1165],{"class":659},"subcommand",[562,1167,1026],{"class":647},[562,1169,1170,1173,1175,1178],{"class":564,"line":892},[562,1171,1172],{"class":659},"    command",[562,1174,1144],{"class":647},[562,1176,1177],{"class":568}," Commands",[562,1179,1150],{"class":647},[562,1181,1182],{"class":564,"line":906},[562,1183,1184],{"class":647},"}\n",[562,1186,1187],{"class":564,"line":911},[562,1188,708],{"emptyLinePlaceholder":707},[562,1190,1191,1193,1195,1197,1200,1202,1204],{"class":564,"line":921},[562,1192,1011],{"class":647},[562,1194,773],{"class":659},[562,1196,1016],{"class":647},[562,1198,1199],{"class":568},"Subcommand",[562,1201,760],{"class":647},[562,1203,1023],{"class":568},[562,1205,1026],{"class":647},[562,1207,1208,1211,1213],{"class":564,"line":935},[562,1209,1210],{"class":1084},"enum",[562,1212,1177],{"class":568},[562,1214,1091],{"class":647},[562,1216,1218],{"class":564,"line":1217},18,[562,1219,1220],{"class":1096},"    /// Validate a JSON file\n",[562,1222,1224,1227],{"class":564,"line":1223},19,[562,1225,1226],{"class":568},"    Validate",[562,1228,1091],{"class":647},[562,1230,1232],{"class":564,"line":1231},20,[562,1233,1234],{"class":1096},"        /// Path to JSON file\n",[562,1236,1238,1241,1243,1245,1248,1250,1252,1255,1257],{"class":564,"line":1237},21,[562,1239,1240],{"class":647},"        #[",[562,1242,1105],{"class":659},[562,1244,1016],{"class":647},[562,1246,1247],{"class":659},"value_name ",[562,1249,663],{"class":647},[562,1251,666],{"class":647},[562,1253,1254],{"class":572},"FILE",[562,1256,757],{"class":647},[562,1258,1026],{"class":647},[562,1260,1262,1265,1267,1270,1272,1275,1277,1280],{"class":564,"line":1261},22,[562,1263,1264],{"class":659},"        file",[562,1266,1144],{"class":647},[562,1268,1269],{"class":568}," std",[562,1271,976],{"class":647},[562,1273,1274],{"class":568},"path",[562,1276,976],{"class":647},[562,1278,1279],{"class":568},"PathBuf",[562,1281,1150],{"class":647},[562,1283,1285],{"class":564,"line":1284},23,[562,1286,1287],{"class":647},"    },\n",[562,1289,1291],{"class":564,"line":1290},24,[562,1292,1184],{"class":647},[562,1294,1296],{"class":564,"line":1295},25,[562,1297,708],{"emptyLinePlaceholder":707},[562,1299,1301,1304,1307,1310,1313,1316,1319],{"class":564,"line":1300},26,[562,1302,1303],{"class":969},"fn",[562,1305,1306],{"class":627}," main",[562,1308,1309],{"class":647},"()",[562,1311,1312],{"class":647}," ->",[562,1314,1315],{"class":568}," Result",[562,1317,1318],{"class":647},"\u003C()>",[562,1320,1091],{"class":647},[562,1322,1324,1327,1330,1332,1334,1336,1339],{"class":564,"line":1323},27,[562,1325,1326],{"class":1084},"    let",[562,1328,1329],{"class":659}," cli ",[562,1331,663],{"class":647},[562,1333,1088],{"class":568},[562,1335,976],{"class":647},[562,1337,1338],{"class":627},"parse",[562,1340,1341],{"class":647},"();\n",[562,1343,1345,1348,1350,1353,1356,1359],{"class":564,"line":1344},28,[562,1346,1347],{"class":627},"    init_tracing",[562,1349,1016],{"class":647},[562,1351,1352],{"class":659},"cli",[562,1354,1355],{"class":647},".",[562,1357,1358],{"class":659},"verbose",[562,1360,1361],{"class":647},");\n",[562,1363,1365],{"class":564,"line":1364},29,[562,1366,708],{"emptyLinePlaceholder":707},[562,1368,1370,1374,1377,1379,1382],{"class":564,"line":1369},30,[562,1371,1373],{"class":1372},"s7zQu","    match",[562,1375,1376],{"class":659}," cli",[562,1378,1355],{"class":647},[562,1380,1381],{"class":659},"command ",[562,1383,1384],{"class":647},"{\n",[562,1386,1388,1391,1393,1396,1398,1401,1404,1407,1410,1413,1416],{"class":564,"line":1387},31,[562,1389,1390],{"class":568},"        Commands",[562,1392,976],{"class":647},[562,1394,1395],{"class":568},"Validate",[562,1397,744],{"class":647},[562,1399,1400],{"class":659}," file ",[562,1402,1403],{"class":647},"}",[562,1405,1406],{"class":647}," =>",[562,1408,1409],{"class":627}," validate_file",[562,1411,1412],{"class":647},"(&",[562,1414,1415],{"class":659},"file",[562,1417,1418],{"class":647},")?,\n",[562,1420,1422],{"class":564,"line":1421},32,[562,1423,1424],{"class":647},"    }\n",[562,1426,1428],{"class":564,"line":1427},33,[562,1429,708],{"emptyLinePlaceholder":707},[562,1431,1433,1436],{"class":564,"line":1432},34,[562,1434,1435],{"class":568},"    Ok",[562,1437,1438],{"class":647},"(())\n",[562,1440,1442],{"class":564,"line":1441},35,[562,1443,1184],{"class":647},[472,1445,1447],{"id":1446},"step-3-add-robust-error-handling","Step 3: Add robust error handling",[468,1449,1450,1451,1454],{},"Use typed domain errors internally, then bubble to ",[487,1452,1453],{},"anyhow::Result"," at boundaries.",[552,1456,1459],{"className":959,"code":1457,"filename":1458,"language":962,"meta":558,"style":558},"use thiserror::Error;\n\n#[derive(Debug, Error)]\npub enum ValidationError {\n    #[error(\"failed to read file '{path}': {source}\")]\n    Io {\n        path: String,\n        #[source]\n        source: std::io::Error,\n    },\n\n    #[error(\"invalid JSON in '{path}': {source}\")]\n    Json {\n        path: String,\n        #[source]\n        source: serde_json::Error,\n    },\n}\n","src/error.rs",[487,1460,1461,1475,1479,1497,1510,1544,1551,1563,1571,1591,1595,1599,1628,1635,1645,1653,1668,1672],{"__ignoreMap":558},[562,1462,1463,1465,1468,1470,1473],{"class":564,"line":565},[562,1464,970],{"class":969},[562,1466,1467],{"class":568}," thiserror",[562,1469,976],{"class":647},[562,1471,1472],{"class":568},"Error",[562,1474,981],{"class":647},[562,1476,1477],{"class":564,"line":576},[562,1478,708],{"emptyLinePlaceholder":707},[562,1480,1481,1483,1485,1487,1490,1492,1495],{"class":564,"line":591},[562,1482,1011],{"class":647},[562,1484,773],{"class":659},[562,1486,1016],{"class":647},[562,1488,1489],{"class":568},"Debug",[562,1491,760],{"class":647},[562,1493,1494],{"class":568}," Error",[562,1496,1026],{"class":647},[562,1498,1499,1502,1505,1508],{"class":564,"line":689},[562,1500,1501],{"class":969},"pub",[562,1503,1504],{"class":1084}," enum",[562,1506,1507],{"class":568}," ValidationError",[562,1509,1091],{"class":647},[562,1511,1512,1514,1517,1519,1521,1524,1527,1529,1531,1534,1536,1539,1542],{"class":564,"line":704},[562,1513,1102],{"class":647},[562,1515,1516],{"class":659},"error",[562,1518,1016],{"class":647},[562,1520,757],{"class":647},[562,1522,1523],{"class":572},"failed to read file '",[562,1525,1526],{"class":647},"{",[562,1528,1274],{"class":572},[562,1530,1403],{"class":647},[562,1532,1533],{"class":572},"': ",[562,1535,1526],{"class":647},[562,1537,1538],{"class":572},"source",[562,1540,1541],{"class":647},"}\"",[562,1543,1026],{"class":647},[562,1545,1546,1549],{"class":564,"line":711},[562,1547,1548],{"class":568},"    Io",[562,1550,1091],{"class":647},[562,1552,1553,1556,1558,1561],{"class":564,"line":721},[562,1554,1555],{"class":659},"        path",[562,1557,1144],{"class":647},[562,1559,1560],{"class":568}," String",[562,1562,1150],{"class":647},[562,1564,1565,1567,1569],{"class":564,"line":736},[562,1566,1240],{"class":647},[562,1568,1538],{"class":659},[562,1570,654],{"class":647},[562,1572,1573,1576,1578,1580,1582,1585,1587,1589],{"class":564,"line":784},[562,1574,1575],{"class":659},"        source",[562,1577,1144],{"class":647},[562,1579,1269],{"class":568},[562,1581,976],{"class":647},[562,1583,1584],{"class":568},"io",[562,1586,976],{"class":647},[562,1588,1472],{"class":568},[562,1590,1150],{"class":647},[562,1592,1593],{"class":564,"line":799},[562,1594,1287],{"class":647},[562,1596,1597],{"class":564,"line":814},[562,1598,708],{"emptyLinePlaceholder":707},[562,1600,1601,1603,1605,1607,1609,1612,1614,1616,1618,1620,1622,1624,1626],{"class":564,"line":854},[562,1602,1102],{"class":647},[562,1604,1516],{"class":659},[562,1606,1016],{"class":647},[562,1608,757],{"class":647},[562,1610,1611],{"class":572},"invalid JSON in '",[562,1613,1526],{"class":647},[562,1615,1274],{"class":572},[562,1617,1403],{"class":647},[562,1619,1533],{"class":572},[562,1621,1526],{"class":647},[562,1623,1538],{"class":572},[562,1625,1541],{"class":647},[562,1627,1026],{"class":647},[562,1629,1630,1633],{"class":564,"line":892},[562,1631,1632],{"class":568},"    Json",[562,1634,1091],{"class":647},[562,1636,1637,1639,1641,1643],{"class":564,"line":906},[562,1638,1555],{"class":659},[562,1640,1144],{"class":647},[562,1642,1560],{"class":568},[562,1644,1150],{"class":647},[562,1646,1647,1649,1651],{"class":564,"line":911},[562,1648,1240],{"class":647},[562,1650,1538],{"class":659},[562,1652,654],{"class":647},[562,1654,1655,1657,1659,1662,1664,1666],{"class":564,"line":921},[562,1656,1575],{"class":659},[562,1658,1144],{"class":647},[562,1660,1661],{"class":568}," serde_json",[562,1663,976],{"class":647},[562,1665,1472],{"class":568},[562,1667,1150],{"class":647},[562,1669,1670],{"class":564,"line":935},[562,1671,1287],{"class":647},[562,1673,1674],{"class":564,"line":1217},[562,1675,1184],{"class":647},[552,1677,1679],{"className":959,"code":1678,"filename":961,"language":962,"meta":558,"style":558},"mod error;\n\nuse anyhow::{Context, Result};\nuse serde_json::Value;\nuse std::fs;\nuse std::path::Path;\nuse tracing::{debug, info};\n\nfn validate_file(path: &Path) -> Result\u003C()> {\n    let display = path.display().to_string();\n\n    let raw = fs::read_to_string(path)\n        .with_context(|| format!(\"unable to read input file: {display}\"))?;\n\n    let json: Value = serde_json::from_str(&raw)\n        .with_context(|| format!(\"unable to parse JSON: {display}\"))?;\n\n    debug!(keys = ?json.as_object().map(|o| o.keys().count()), \"json parsed\");\n    info!(file = %display, \"validation successful\");\n    println!(\"OK: {display}\");\n    Ok(())\n}\n",[487,1680,1681,1691,1695,1712,1725,1738,1755,1774,1778,1805,1830,1834,1858,1888,1892,1921,1946,1950,2012,2040,2060,2066],{"__ignoreMap":558},[562,1682,1683,1686,1689],{"class":564,"line":565},[562,1684,1685],{"class":1084},"mod",[562,1687,1688],{"class":568}," error",[562,1690,981],{"class":647},[562,1692,1693],{"class":564,"line":576},[562,1694,708],{"emptyLinePlaceholder":707},[562,1696,1697,1699,1701,1703,1706,1708,1710],{"class":564,"line":591},[562,1698,970],{"class":969},[562,1700,973],{"class":568},[562,1702,991],{"class":647},[562,1704,1705],{"class":568},"Context",[562,1707,760],{"class":647},[562,1709,1315],{"class":568},[562,1711,1002],{"class":647},[562,1713,1714,1716,1718,1720,1723],{"class":564,"line":689},[562,1715,970],{"class":969},[562,1717,1661],{"class":568},[562,1719,976],{"class":647},[562,1721,1722],{"class":568},"Value",[562,1724,981],{"class":647},[562,1726,1727,1729,1731,1733,1736],{"class":564,"line":704},[562,1728,970],{"class":969},[562,1730,1269],{"class":568},[562,1732,976],{"class":647},[562,1734,1735],{"class":568},"fs",[562,1737,981],{"class":647},[562,1739,1740,1742,1744,1746,1748,1750,1753],{"class":564,"line":711},[562,1741,970],{"class":969},[562,1743,1269],{"class":568},[562,1745,976],{"class":647},[562,1747,1274],{"class":568},[562,1749,976],{"class":647},[562,1751,1752],{"class":568},"Path",[562,1754,981],{"class":647},[562,1756,1757,1759,1762,1764,1767,1769,1772],{"class":564,"line":721},[562,1758,970],{"class":969},[562,1760,1761],{"class":568}," tracing",[562,1763,991],{"class":647},[562,1765,1766],{"class":568},"debug",[562,1768,760],{"class":647},[562,1770,1771],{"class":568}," info",[562,1773,1002],{"class":647},[562,1775,1776],{"class":564,"line":736},[562,1777,708],{"emptyLinePlaceholder":707},[562,1779,1780,1782,1784,1786,1788,1790,1793,1795,1797,1799,1801,1803],{"class":564,"line":784},[562,1781,1303],{"class":969},[562,1783,1409],{"class":627},[562,1785,1016],{"class":647},[562,1787,1274],{"class":659},[562,1789,1144],{"class":647},[562,1791,1792],{"class":647}," &",[562,1794,1752],{"class":568},[562,1796,524],{"class":647},[562,1798,1312],{"class":647},[562,1800,1315],{"class":568},[562,1802,1318],{"class":647},[562,1804,1091],{"class":647},[562,1806,1807,1809,1812,1814,1817,1819,1822,1825,1828],{"class":564,"line":799},[562,1808,1326],{"class":1084},[562,1810,1811],{"class":659}," display ",[562,1813,663],{"class":647},[562,1815,1816],{"class":659}," path",[562,1818,1355],{"class":647},[562,1820,1821],{"class":627},"display",[562,1823,1824],{"class":647},"().",[562,1826,1827],{"class":627},"to_string",[562,1829,1341],{"class":647},[562,1831,1832],{"class":564,"line":814},[562,1833,708],{"emptyLinePlaceholder":707},[562,1835,1836,1838,1841,1843,1846,1848,1851,1853,1855],{"class":564,"line":854},[562,1837,1326],{"class":1084},[562,1839,1840],{"class":659}," raw ",[562,1842,663],{"class":647},[562,1844,1845],{"class":568}," fs",[562,1847,976],{"class":647},[562,1849,1850],{"class":627},"read_to_string",[562,1852,1016],{"class":647},[562,1854,1274],{"class":659},[562,1856,1857],{"class":647},")\n",[562,1859,1860,1863,1866,1869,1872,1874,1876,1879,1881,1883,1885],{"class":564,"line":892},[562,1861,1862],{"class":647},"        .",[562,1864,1865],{"class":627},"with_context",[562,1867,1868],{"class":647},"(||",[562,1870,1871],{"class":627}," format!",[562,1873,1016],{"class":647},[562,1875,757],{"class":647},[562,1877,1878],{"class":572},"unable to read input file: ",[562,1880,1526],{"class":647},[562,1882,1821],{"class":572},[562,1884,1541],{"class":647},[562,1886,1887],{"class":647},"))?;\n",[562,1889,1890],{"class":564,"line":906},[562,1891,708],{"emptyLinePlaceholder":707},[562,1893,1894,1896,1899,1901,1904,1907,1909,1911,1914,1916,1919],{"class":564,"line":911},[562,1895,1326],{"class":1084},[562,1897,1898],{"class":659}," json",[562,1900,1144],{"class":647},[562,1902,1903],{"class":568}," Value",[562,1905,1906],{"class":647}," =",[562,1908,1661],{"class":568},[562,1910,976],{"class":647},[562,1912,1913],{"class":627},"from_str",[562,1915,1412],{"class":647},[562,1917,1918],{"class":659},"raw",[562,1920,1857],{"class":647},[562,1922,1923,1925,1927,1929,1931,1933,1935,1938,1940,1942,1944],{"class":564,"line":921},[562,1924,1862],{"class":647},[562,1926,1865],{"class":627},[562,1928,1868],{"class":647},[562,1930,1871],{"class":627},[562,1932,1016],{"class":647},[562,1934,757],{"class":647},[562,1936,1937],{"class":572},"unable to parse JSON: ",[562,1939,1526],{"class":647},[562,1941,1821],{"class":572},[562,1943,1541],{"class":647},[562,1945,1887],{"class":647},[562,1947,1948],{"class":564,"line":935},[562,1949,708],{"emptyLinePlaceholder":707},[562,1951,1952,1955,1957,1960,1962,1965,1968,1970,1973,1975,1978,1981,1984,1987,1990,1992,1995,1997,2000,2003,2005,2008,2010],{"class":564,"line":1217},[562,1953,1954],{"class":627},"    debug!",[562,1956,1016],{"class":647},[562,1958,1959],{"class":659},"keys ",[562,1961,663],{"class":647},[562,1963,1964],{"class":647}," ?",[562,1966,1967],{"class":659},"json",[562,1969,1355],{"class":647},[562,1971,1972],{"class":627},"as_object",[562,1974,1824],{"class":647},[562,1976,1977],{"class":627},"map",[562,1979,1980],{"class":647},"(|",[562,1982,1983],{"class":659},"o",[562,1985,1986],{"class":647},"|",[562,1988,1989],{"class":659}," o",[562,1991,1355],{"class":647},[562,1993,1994],{"class":627},"keys",[562,1996,1824],{"class":647},[562,1998,1999],{"class":627},"count",[562,2001,2002],{"class":647},"()),",[562,2004,666],{"class":647},[562,2006,2007],{"class":572},"json parsed",[562,2009,757],{"class":647},[562,2011,1361],{"class":647},[562,2013,2014,2017,2019,2022,2024,2027,2029,2031,2033,2036,2038],{"class":564,"line":1223},[562,2015,2016],{"class":627},"    info!",[562,2018,1016],{"class":647},[562,2020,2021],{"class":659},"file ",[562,2023,663],{"class":647},[562,2025,2026],{"class":647}," %",[562,2028,1821],{"class":659},[562,2030,760],{"class":647},[562,2032,666],{"class":647},[562,2034,2035],{"class":572},"validation successful",[562,2037,757],{"class":647},[562,2039,1361],{"class":647},[562,2041,2042,2045,2047,2049,2052,2054,2056,2058],{"class":564,"line":1231},[562,2043,2044],{"class":627},"    println!",[562,2046,1016],{"class":647},[562,2048,757],{"class":647},[562,2050,2051],{"class":572},"OK: ",[562,2053,1526],{"class":647},[562,2055,1821],{"class":572},[562,2057,1541],{"class":647},[562,2059,1361],{"class":647},[562,2061,2062,2064],{"class":564,"line":1237},[562,2063,1435],{"class":568},[562,2065,1438],{"class":647},[562,2067,2068],{"class":564,"line":1261},[562,2069,1184],{"class":647},[472,2071,2073],{"id":2072},"step-4-configure-logging-and-runtime-ergonomics","Step 4: Configure logging and runtime ergonomics",[552,2075,2077],{"className":959,"code":2076,"filename":961,"language":962,"meta":558,"style":558},"fn init_tracing(verbose: u8) {\n    let level = match verbose {\n        0 => \"info\",\n        1 => \"debug\",\n        _ => \"trace\",\n    };\n\n    tracing_subscriber::fmt()\n        .with_env_filter(\n            tracing_subscriber::EnvFilter::try_from_default_env()\n                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(level)),\n        )\n        .with_target(false)\n        .compact()\n        .init();\n}\n",[487,2078,2079,2098,2115,2131,2146,2163,2168,2172,2185,2195,2212,2247,2252,2262,2271,2280],{"__ignoreMap":558},[562,2080,2081,2083,2086,2088,2090,2092,2094,2096],{"class":564,"line":565},[562,2082,1303],{"class":969},[562,2084,2085],{"class":627}," init_tracing",[562,2087,1016],{"class":647},[562,2089,1358],{"class":659},[562,2091,1144],{"class":647},[562,2093,1147],{"class":568},[562,2095,524],{"class":647},[562,2097,1091],{"class":647},[562,2099,2100,2102,2105,2107,2110,2113],{"class":564,"line":576},[562,2101,1326],{"class":1084},[562,2103,2104],{"class":659}," level ",[562,2106,663],{"class":647},[562,2108,2109],{"class":1372}," match",[562,2111,2112],{"class":659}," verbose ",[562,2114,1384],{"class":647},[562,2116,2117,2120,2122,2124,2127,2129],{"class":564,"line":591},[562,2118,2119],{"class":969},"        0",[562,2121,1406],{"class":647},[562,2123,666],{"class":647},[562,2125,2126],{"class":572},"info",[562,2128,757],{"class":647},[562,2130,1150],{"class":647},[562,2132,2133,2136,2138,2140,2142,2144],{"class":564,"line":689},[562,2134,2135],{"class":969},"        1",[562,2137,1406],{"class":647},[562,2139,666],{"class":647},[562,2141,1766],{"class":572},[562,2143,757],{"class":647},[562,2145,1150],{"class":647},[562,2147,2148,2151,2154,2156,2159,2161],{"class":564,"line":704},[562,2149,2150],{"class":659},"        _ ",[562,2152,2153],{"class":647},"=>",[562,2155,666],{"class":647},[562,2157,2158],{"class":572},"trace",[562,2160,757],{"class":647},[562,2162,1150],{"class":647},[562,2164,2165],{"class":564,"line":711},[562,2166,2167],{"class":647},"    };\n",[562,2169,2170],{"class":564,"line":721},[562,2171,708],{"emptyLinePlaceholder":707},[562,2173,2174,2177,2179,2182],{"class":564,"line":736},[562,2175,2176],{"class":568},"    tracing_subscriber",[562,2178,976],{"class":647},[562,2180,2181],{"class":627},"fmt",[562,2183,2184],{"class":647},"()\n",[562,2186,2187,2189,2192],{"class":564,"line":784},[562,2188,1862],{"class":647},[562,2190,2191],{"class":627},"with_env_filter",[562,2193,2194],{"class":647},"(\n",[562,2196,2197,2200,2202,2205,2207,2210],{"class":564,"line":799},[562,2198,2199],{"class":568},"            tracing_subscriber",[562,2201,976],{"class":647},[562,2203,2204],{"class":568},"EnvFilter",[562,2206,976],{"class":647},[562,2208,2209],{"class":627},"try_from_default_env",[562,2211,2184],{"class":647},[562,2213,2214,2217,2220,2222,2225,2227,2230,2232,2234,2236,2239,2241,2244],{"class":564,"line":814},[562,2215,2216],{"class":647},"                .",[562,2218,2219],{"class":627},"unwrap_or_else",[562,2221,1980],{"class":647},[562,2223,2224],{"class":659},"_",[562,2226,1986],{"class":647},[562,2228,2229],{"class":568}," tracing_subscriber",[562,2231,976],{"class":647},[562,2233,2204],{"class":568},[562,2235,976],{"class":647},[562,2237,2238],{"class":627},"new",[562,2240,1016],{"class":647},[562,2242,2243],{"class":659},"level",[562,2245,2246],{"class":647},")),\n",[562,2248,2249],{"class":564,"line":854},[562,2250,2251],{"class":647},"        )\n",[562,2253,2254,2256,2259],{"class":564,"line":892},[562,2255,1862],{"class":647},[562,2257,2258],{"class":627},"with_target",[562,2260,2261],{"class":647},"(false)\n",[562,2263,2264,2266,2269],{"class":564,"line":906},[562,2265,1862],{"class":647},[562,2267,2268],{"class":627},"compact",[562,2270,2184],{"class":647},[562,2272,2273,2275,2278],{"class":564,"line":911},[562,2274,1862],{"class":647},[562,2276,2277],{"class":627},"init",[562,2279,1341],{"class":647},[562,2281,2282],{"class":564,"line":921},[562,2283,1184],{"class":647},[468,2285,2286],{},"Usage:",[552,2288,2290],{"className":554,"code":2289,"filename":556,"language":557,"meta":558,"style":558},"cargo run -- validate ./example.json\nRUST_LOG=debug cargo run -- validate ./example.json\n",[487,2291,2292,2308],{"__ignoreMap":558},[562,2293,2294,2296,2299,2302,2305],{"class":564,"line":565},[562,2295,579],{"class":568},[562,2297,2298],{"class":572}," run",[562,2300,2301],{"class":572}," --",[562,2303,2304],{"class":572}," validate",[562,2306,2307],{"class":572}," ./example.json\n",[562,2309,2310,2313,2315,2317,2320,2322,2324,2326],{"class":564,"line":576},[562,2311,2312],{"class":659},"RUST_LOG",[562,2314,663],{"class":647},[562,2316,1766],{"class":572},[562,2318,2319],{"class":568}," cargo",[562,2321,2298],{"class":572},[562,2323,2301],{"class":572},[562,2325,2304],{"class":572},[562,2327,2307],{"class":572},[472,2329,2331],{"id":2330},"step-5-add-cli-behavior-tests","Step 5: Add CLI behavior tests",[468,2333,2334],{},"Use integration tests to verify user-facing behavior.",[552,2336,2339],{"className":959,"code":2337,"filename":2338,"language":962,"meta":558,"style":558},"use assert_cmd::Command;\nuse predicates::str::contains;\n\n#[test]\nfn validate_succeeds_for_valid_json() {\n    let mut cmd = Command::cargo_bin(\"devtool\").unwrap();\n    cmd.arg(\"validate\")\n        .arg(\"tests/fixtures/ok.json\")\n        .assert()\n        .success()\n        .stdout(contains(\"OK:\"));\n}\n\n#[test]\nfn validate_fails_for_bad_json() {\n    let mut cmd = Command::cargo_bin(\"devtool\").unwrap();\n    cmd.arg(\"validate\")\n        .arg(\"tests/fixtures/bad.json\")\n        .assert()\n        .failure()\n        .stderr(contains(\"unable to parse JSON\"));\n}\n","tests/validate_cli.rs",[487,2340,2341,2355,2374,2378,2387,2398,2434,2454,2471,2480,2489,2512,2516,2520,2528,2539,2569,2587,2604,2612,2621,2643],{"__ignoreMap":558},[562,2342,2343,2345,2348,2350,2353],{"class":564,"line":565},[562,2344,970],{"class":969},[562,2346,2347],{"class":568}," assert_cmd",[562,2349,976],{"class":647},[562,2351,2352],{"class":568},"Command",[562,2354,981],{"class":647},[562,2356,2357,2359,2362,2364,2367,2369,2372],{"class":564,"line":576},[562,2358,970],{"class":969},[562,2360,2361],{"class":568}," predicates",[562,2363,976],{"class":647},[562,2365,2366],{"class":568},"str",[562,2368,976],{"class":647},[562,2370,2371],{"class":568},"contains",[562,2373,981],{"class":647},[562,2375,2376],{"class":564,"line":591},[562,2377,708],{"emptyLinePlaceholder":707},[562,2379,2380,2382,2385],{"class":564,"line":689},[562,2381,1011],{"class":647},[562,2383,2384],{"class":659},"test",[562,2386,654],{"class":647},[562,2388,2389,2391,2394,2396],{"class":564,"line":704},[562,2390,1303],{"class":969},[562,2392,2393],{"class":627}," validate_succeeds_for_valid_json",[562,2395,1309],{"class":647},[562,2397,1091],{"class":647},[562,2399,2400,2402,2405,2408,2410,2413,2415,2418,2420,2422,2424,2426,2429,2432],{"class":564,"line":711},[562,2401,1326],{"class":1084},[562,2403,2404],{"class":1084}," mut",[562,2406,2407],{"class":659}," cmd ",[562,2409,663],{"class":647},[562,2411,2412],{"class":568}," Command",[562,2414,976],{"class":647},[562,2416,2417],{"class":627},"cargo_bin",[562,2419,1016],{"class":647},[562,2421,757],{"class":647},[562,2423,669],{"class":572},[562,2425,757],{"class":647},[562,2427,2428],{"class":647},").",[562,2430,2431],{"class":627},"unwrap",[562,2433,1341],{"class":647},[562,2435,2436,2439,2441,2443,2445,2447,2450,2452],{"class":564,"line":721},[562,2437,2438],{"class":659},"    cmd",[562,2440,1355],{"class":647},[562,2442,1105],{"class":627},[562,2444,1016],{"class":647},[562,2446,757],{"class":647},[562,2448,2449],{"class":572},"validate",[562,2451,757],{"class":647},[562,2453,1857],{"class":647},[562,2455,2456,2458,2460,2462,2464,2467,2469],{"class":564,"line":736},[562,2457,1862],{"class":647},[562,2459,1105],{"class":627},[562,2461,1016],{"class":647},[562,2463,757],{"class":647},[562,2465,2466],{"class":572},"tests/fixtures/ok.json",[562,2468,757],{"class":647},[562,2470,1857],{"class":647},[562,2472,2473,2475,2478],{"class":564,"line":784},[562,2474,1862],{"class":647},[562,2476,2477],{"class":627},"assert",[562,2479,2184],{"class":647},[562,2481,2482,2484,2487],{"class":564,"line":799},[562,2483,1862],{"class":647},[562,2485,2486],{"class":627},"success",[562,2488,2184],{"class":647},[562,2490,2491,2493,2496,2498,2500,2502,2504,2507,2509],{"class":564,"line":814},[562,2492,1862],{"class":647},[562,2494,2495],{"class":627},"stdout",[562,2497,1016],{"class":647},[562,2499,2371],{"class":627},[562,2501,1016],{"class":647},[562,2503,757],{"class":647},[562,2505,2506],{"class":572},"OK:",[562,2508,757],{"class":647},[562,2510,2511],{"class":647},"));\n",[562,2513,2514],{"class":564,"line":854},[562,2515,1184],{"class":647},[562,2517,2518],{"class":564,"line":892},[562,2519,708],{"emptyLinePlaceholder":707},[562,2521,2522,2524,2526],{"class":564,"line":906},[562,2523,1011],{"class":647},[562,2525,2384],{"class":659},[562,2527,654],{"class":647},[562,2529,2530,2532,2535,2537],{"class":564,"line":911},[562,2531,1303],{"class":969},[562,2533,2534],{"class":627}," validate_fails_for_bad_json",[562,2536,1309],{"class":647},[562,2538,1091],{"class":647},[562,2540,2541,2543,2545,2547,2549,2551,2553,2555,2557,2559,2561,2563,2565,2567],{"class":564,"line":921},[562,2542,1326],{"class":1084},[562,2544,2404],{"class":1084},[562,2546,2407],{"class":659},[562,2548,663],{"class":647},[562,2550,2412],{"class":568},[562,2552,976],{"class":647},[562,2554,2417],{"class":627},[562,2556,1016],{"class":647},[562,2558,757],{"class":647},[562,2560,669],{"class":572},[562,2562,757],{"class":647},[562,2564,2428],{"class":647},[562,2566,2431],{"class":627},[562,2568,1341],{"class":647},[562,2570,2571,2573,2575,2577,2579,2581,2583,2585],{"class":564,"line":935},[562,2572,2438],{"class":659},[562,2574,1355],{"class":647},[562,2576,1105],{"class":627},[562,2578,1016],{"class":647},[562,2580,757],{"class":647},[562,2582,2449],{"class":572},[562,2584,757],{"class":647},[562,2586,1857],{"class":647},[562,2588,2589,2591,2593,2595,2597,2600,2602],{"class":564,"line":1217},[562,2590,1862],{"class":647},[562,2592,1105],{"class":627},[562,2594,1016],{"class":647},[562,2596,757],{"class":647},[562,2598,2599],{"class":572},"tests/fixtures/bad.json",[562,2601,757],{"class":647},[562,2603,1857],{"class":647},[562,2605,2606,2608,2610],{"class":564,"line":1223},[562,2607,1862],{"class":647},[562,2609,2477],{"class":627},[562,2611,2184],{"class":647},[562,2613,2614,2616,2619],{"class":564,"line":1231},[562,2615,1862],{"class":647},[562,2617,2618],{"class":627},"failure",[562,2620,2184],{"class":647},[562,2622,2623,2625,2628,2630,2632,2634,2636,2639,2641],{"class":564,"line":1237},[562,2624,1862],{"class":647},[562,2626,2627],{"class":627},"stderr",[562,2629,1016],{"class":647},[562,2631,2371],{"class":627},[562,2633,1016],{"class":647},[562,2635,757],{"class":647},[562,2637,2638],{"class":572},"unable to parse JSON",[562,2640,757],{"class":647},[562,2642,2511],{"class":647},[562,2644,2645],{"class":564,"line":1261},[562,2646,1184],{"class":647},[468,2648,2649],{},"Fixture files:",[552,2651,2654],{"className":2652,"code":2653,"filename":2466,"language":1967,"meta":558,"style":558},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\"name\":\"devtool\",\"version\":1}\n",[487,2655,2656],{"__ignoreMap":558},[562,2657,2658,2660,2662,2665,2667,2669,2671,2673,2675,2677,2679,2682,2684,2686,2688],{"class":564,"line":565},[562,2659,1526],{"class":647},[562,2661,757],{"class":647},[562,2663,2664],{"class":1084},"name",[562,2666,757],{"class":647},[562,2668,1144],{"class":647},[562,2670,757],{"class":647},[562,2672,669],{"class":572},[562,2674,757],{"class":647},[562,2676,760],{"class":647},[562,2678,757],{"class":647},[562,2680,2681],{"class":1084},"version",[562,2683,757],{"class":647},[562,2685,1144],{"class":647},[562,2687,731],{"class":969},[562,2689,1184],{"class":647},[552,2691,2693],{"className":2652,"code":2692,"filename":2599,"language":1967,"meta":558,"style":558},"{\"name\":\n",[487,2694,2695],{"__ignoreMap":558},[562,2696,2697,2699,2701,2703,2705],{"class":564,"line":565},[562,2698,1526],{"class":647},[562,2700,757],{"class":647},[562,2702,2664],{"class":1084},[562,2704,757],{"class":647},[562,2706,2707],{"class":647},":\n",[472,2709,2711],{"id":2710},"common-pitfalls","Common pitfalls",[2713,2714,2716,2717,2720],"h3",{"id":2715},"_1-returning-string-errors-everywhere","1) Returning ",[487,2718,2719],{},"String"," errors everywhere",[468,2722,2723,2726,2727,2729,2730,2428],{},[487,2724,2725],{},"Result\u003CT, String>"," loses context and source chains. Prefer typed errors (",[487,2728,499],{},") plus context (",[487,2731,2732],{},"anyhow::Context",[2713,2734,2736],{"id":2735},"_2-panicking-on-expected-user-mistakes","2) Panicking on expected user mistakes",[468,2738,2739,2740,1355],{},"Missing files and parse failures are normal CLI outcomes. Return informative errors and non-zero exit codes instead of ",[487,2741,2742],{},"panic!",[2713,2744,2746],{"id":2745},"_3-skipping-subcommands-too-long","3) Skipping subcommands too long",[468,2748,2749,2750,2752,2753,2752,2756,2759],{},"A flat CLI with many flags gets unmaintainable quickly. Use subcommands early (",[487,2751,2449],{},", ",[487,2754,2755],{},"format",[487,2757,2758],{},"sync",", etc.).",[2713,2761,2763],{"id":2762},"_4-no-integration-tests","4) No integration tests",[468,2765,2766],{},"Unit tests alone won’t catch argument parsing, exit code behavior, or output regressions.",[2713,2768,2770],{"id":2769},"_5-uncontrolled-log-noise","5) Uncontrolled log noise",[468,2772,2773,2774,2776,2777,2780,2781,1355],{},"Default to concise logs (",[487,2775,2126],{},") and make debug output opt-in with ",[487,2778,2779],{},"-v","/",[487,2782,2312],{},[472,2784,2786],{"id":2785},"validation-checklist","Validation checklist",[468,2788,2789],{},"Before publishing or sharing your CLI:",[480,2791,2794,2806,2814,2826,2835,2841],{"className":2792},[2793],"contains-task-list",[483,2795,2798,2802,2803],{"className":2796},[2797],"task-list-item",[2799,2800],"input",{"disabled":707,"type":2801},"checkbox"," ",[487,2804,2805],{},"cargo fmt --check",[483,2807,2809,2802,2811],{"className":2808},[2797],[2799,2810],{"disabled":707,"type":2801},[487,2812,2813],{},"cargo clippy --all-targets --all-features -- -D warnings",[483,2815,2817,2802,2819,2822,2823,524],{"className":2816},[2797],[2799,2818],{"disabled":707,"type":2801},[487,2820,2821],{},"cargo test"," (or ",[487,2824,2825],{},"cargo nextest run",[483,2827,2829,2802,2831,2834],{"className":2828},[2797],[2799,2830],{"disabled":707,"type":2801},[487,2832,2833],{},"cargo run -- --help"," shows clear command and examples",[483,2836,2838,2840],{"className":2837},[2797],[2799,2839],{"disabled":707,"type":2801}," Invalid input returns non-zero status + actionable message",[483,2842,2844,2846],{"className":2843},[2797],[2799,2845],{"disabled":707,"type":2801}," README includes install and quick-start usage",[468,2848,2849],{},"Optional release hardening:",[480,2851,2853,2866,2875],{"className":2852},[2793],[483,2854,2856,2858,2859,2862,2863,524],{"className":2855},[2797],[2799,2857],{"disabled":707,"type":2801}," Add ",[487,2860,2861],{},"--locked"," in CI builds (",[487,2864,2865],{},"cargo build --locked",[483,2867,2869,2871,2872,524],{"className":2868},[2797],[2799,2870],{"disabled":707,"type":2801}," Build release binary and smoke test (",[487,2873,2874],{},"cargo build --release",[483,2876,2878,2880],{"className":2877},[2797],[2799,2879],{"disabled":707,"type":2801}," Tag and publish with changelog if this is a public crate",[472,2882,2884],{"id":2883},"summary","Summary",[468,2886,2887],{},"A shippable Rust CLI is mostly about operational discipline:",[480,2889,2890,2895,2903,2908],{},[483,2891,2892,2893,524],{},"clear command design (",[487,2894,489],{},[483,2896,2897,2898,2900,2901,524],{},"strong errors (",[487,2899,499],{}," + ",[487,2902,495],{},[483,2904,2905,2906,524],{},"controllable observability (",[487,2907,505],{},[483,2909,2910],{},"integration tests for real user flows",[468,2912,2913],{},"If you standardize this structure across your tools, new CLIs become faster to ship and easier to maintain.",[2915,2916,2917],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":558,"searchDepth":576,"depth":576,"links":2919},[2920,2921,2922,2923,2925,2926,2927,2928,2936,2937],{"id":474,"depth":576,"text":475},{"id":514,"depth":576,"text":515},{"id":603,"depth":576,"text":604},{"id":950,"depth":576,"text":2924},"Step 2: Define CLI shape with clap",{"id":1446,"depth":576,"text":1447},{"id":2072,"depth":576,"text":2073},{"id":2330,"depth":576,"text":2331},{"id":2710,"depth":576,"text":2711,"children":2929},[2930,2932,2933,2934,2935],{"id":2715,"depth":591,"text":2931},"1) Returning String errors everywhere",{"id":2735,"depth":591,"text":2736},{"id":2745,"depth":591,"text":2746},{"id":2762,"depth":591,"text":2763},{"id":2769,"depth":591,"text":2770},{"id":2785,"depth":576,"text":2786},{"id":2883,"depth":576,"text":2884},"Build a practical Rust CLI with clap, robust errors, structured logging, and release checks","md",null,{},{"title":174,"description":2938},"IYBqePzDRblWsdzIIFBwfvYjKI9kOhCzld_s_ut7ls0",[2945,2947],{"title":170,"path":171,"stem":172,"description":2946,"children":-1},"Practical techniques for finding and fixing bugs in Rust — from print debugging to LLDB and macro expansion",{"title":97,"path":180,"stem":181,"description":2948,"icon":26,"children":-1},"Get started with Python using the uv package manager.",1775772878629]