{"record":{"id":"99984288d7a1a32a","repo":"tonhowtf/omniget","slug":"escolha-a-pasta-de-destino-goodreads","errorCode":null,"errorMessage":"escolha a pasta de destino","messagePattern":"escolha a pasta de destino","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/lists/goodreads.rs","lineNumber":511,"sourceCode":"    let base: String = crate::core::tools::music::norm::strip_accents(s)\n        .to_lowercase()\n        .chars()\n        .map(|c| if c.is_alphanumeric() { c } else { '-' })\n        .collect();\n    let parts: Vec<&str> = base.split('-').filter(|p| !p.is_empty()).collect();\n    let s = parts.join(\"-\");\n    if s.is_empty() {\n        \"prateleira\".to_string()\n    } else {\n        s\n    }\n}\n\n// ── Execução ────────────────────────────────────────────────────────────\n\npub async fn run(opts: &Options, p: ProgressFn) -> Result<ExportResult> {\n    if opts.dest.trim().is_empty() {\n        return Err(anyhow!(\"escolha a pasta de destino\"));\n    }\n    let dest = PathBuf::from(opts.dest.trim());\n    std::fs::create_dir_all(&dest)?;\n    let f = Fetcher::new(opts.delay_ms, opts.session_netscape.as_deref(), DOMAIN)?;\n\n    let (csv, source, waited) = match opts.csv_path.as_deref().map(str::trim) {\n        Some(path) if !path.is_empty() => (\n            std::fs::read_to_string(path).with_context(|| format!(\"não consegui ler {}\", path))?,\n            path.to_string(),\n            0,\n        ),\n        _ => {\n            if !f.has_session() {\n                return Err(anyhow!(\n                    \"sem sessão do Goodreads: capture os cookies de goodreads.com na extensão, ou aponte o goodreads_library_export.csv que você já baixou\"\n                ));\n            }\n            let (csv, secs) = trigger_and_wait(&f, opts, &p).await?;","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/lists/goodreads.rs#L493-L529","documentation":"Thrown at the top of the Goodreads export tool's run() when the required destination folder option is blank or whitespace-only. The tool needs a directory where it writes goodreads_library_export.csv and generated output files, so it refuses to run without one.","triggerScenarios":"Calling run(&opts, p) where opts.dest is \"\" or contains only whitespace (opts.dest.trim().is_empty()).","commonSituations":"UI form left empty before invoking the tool; config file missing the dest key so the default empty string is used; trailing-space-only input that survives validation.","solutions":["Set opts.dest to an existing or creatable directory path before calling run.","Validate the destination field in your UI/config layer and require a non-empty value.","Trim user input and store the trimmed path in Options."],"exampleFix":"// before\nlet opts = Options { dest: cfg.dest, .. };\n// after\nlet dest = cfg.dest.trim();\nif dest.is_empty() { return Err(anyhow!(\"escolha a pasta de destino\")); }\nlet opts = Options { dest: dest.to_string(), .. };","handlingStrategy":"validation","validationCode":"if opts.dest.trim().is_empty() {\n    return Err(anyhow!(\"dest é obrigatório: informe a pasta de saída\"));\n}\nstd::fs::create_dir_all(opts.dest.trim())?;","typeGuard":"fn has_dest(opts: &Options) -> bool {\n    !opts.dest.trim().is_empty()\n}","tryCatchPattern":"match run(&opts, &p).await {\n    Err(e) if e.to_string().contains(\"pasta de destino\") => eprintln!(\"selecione uma pasta de destino antes de exportar\"),\n    other => other?,\n}","preventionTips":["Require the destination field in the UI before enabling the export button","Trim user input when building Options","Provide a sensible default destination directory","Pre-create the destination folder in setup code"],"tags":["validation","argument","goodreads"],"backgroundTag":"empty-required-field","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}