Skip to main content

sqlite_graphrag/i18n/validation/
messages_cli.rs

1//! Messages about CLI ARGUMENTS and flag combinations (GAP-SG-146).
2//!
3//! Missing, conflicting or malformed flags, and the explicit confirmations a
4//! destructive subcommand refuses to run without.
5
6use crate::i18n::{current, Language};
7
8/// Invalid JSON for a CLI flag that takes a file path (e.g. `--entities-file`).
9pub fn invalid_json_in_flag(flag: &str, err: &impl std::fmt::Display) -> String {
10    match current() {
11        Language::English => format!("invalid JSON in {flag}: {err}"),
12        Language::Portuguese => format!("JSON inválido em {flag}: {err}"),
13    }
14}
15
16/// Invalid JSON payload on a stdin flag (e.g. `--graph-stdin`).
17pub fn invalid_json_payload_on_flag(flag: &str, err: &impl std::fmt::Display) -> String {
18    match current() {
19        Language::English => format!("invalid JSON payload on {flag}: {err}"),
20        Language::Portuguese => format!("payload JSON inválido em {flag}: {err}"),
21    }
22}
23
24/// Invalid pending-embeddings status filter.
25pub fn invalid_status_filter(other: &str) -> String {
26    match current() {
27        Language::English => {
28            format!("invalid status filter: {other} (expected pending|in_progress|done|abandoned)")
29        }
30        Language::Portuguese => format!(
31            "filtro de status inválido: {other} (esperado pending|in_progress|done|abandoned)"
32        ),
33    }
34}
35
36/// Localized message for `invalid_tz`.
37pub fn invalid_tz(v: &str) -> String {
38    match current() {
39        Language::English => {
40            format!("display.tz invalid: '{v}'; use an IANA name like 'America/Sao_Paulo'")
41        }
42        Language::Portuguese => {
43            format!("display.tz inválido: '{v}'; use um nome IANA como 'America/Sao_Paulo'")
44        }
45    }
46}
47
48/// `--enable-ner` and `--skip-extraction` conflict.
49pub fn enable_ner_skip_extraction_exclusive() -> String {
50    match current() {
51        Language::English => {
52            "--enable-ner and --skip-extraction are mutually exclusive; remove one".to_string()
53        }
54        Language::Portuguese => {
55            "--enable-ner e --skip-extraction são mutuamente exclusivos; remova um".to_string()
56        }
57    }
58}
59
60/// --entity required when --memory is used.
61pub fn entity_required_when_memory() -> String {
62    match current() {
63        Language::English => "--entity is required when --memory is used".to_string(),
64        Language::Portuguese => "--entity é obrigatório quando --memory é usado".to_string(),
65    }
66}
67
68/// --from required when not using --entity/--all.
69pub fn from_required_without_entity_all() -> String {
70    match current() {
71        Language::English => "--from is required when --entity/--all is not used".to_string(),
72        Language::Portuguese => {
73            "--from é obrigatório quando --entity/--all não é usado".to_string()
74        }
75    }
76}
77
78/// Batch reclassify missing --from-type.
79pub fn from_type_required_batch() -> String {
80    match current() {
81        Language::English => "--from-type is required in batch mode".to_string(),
82        Language::Portuguese => "--from-type é obrigatório no modo batch".to_string(),
83    }
84}
85
86/// --to required when not using --entity/--all.
87pub fn to_required_without_entity_all() -> String {
88    match current() {
89        Language::English => "--to is required when --entity/--all is not used".to_string(),
90        Language::Portuguese => "--to é obrigatório quando --entity/--all não é usado".to_string(),
91    }
92}
93
94/// Batch reclassify missing --to-type.
95pub fn to_type_required_batch() -> String {
96    match current() {
97        Language::English => "--to-type is required in batch mode".to_string(),
98        Language::Portuguese => "--to-type é obrigatório no modo batch".to_string(),
99    }
100}
101
102/// Memory/entity name required as positional or --name.
103pub fn name_required_positional_or_flag() -> String {
104    match current() {
105        Language::English => "name required: pass as positional argument or via --name".to_string(),
106        Language::Portuguese => {
107            "nome obrigatório: passe como argumento posicional ou via --name".to_string()
108        }
109    }
110}
111
112/// Single-mode missing --name.
113pub fn name_required_single_mode() -> String {
114    match current() {
115        Language::English => "--name is required in single mode".to_string(),
116        Language::Portuguese => "--name é obrigatório no modo single".to_string(),
117    }
118}
119
120/// Single-mode missing --target.
121pub fn target_required_single_mode() -> String {
122    match current() {
123        Language::English => "--target is required in single mode".to_string(),
124        Language::Portuguese => "--target é obrigatório no modo single".to_string(),
125    }
126}
127
128/// CREATE path requires `--type` and `--description`.
129pub fn type_and_description_required() -> String {
130    match current() {
131        Language::English => {
132            "--type and --description are required when creating a new memory".to_string()
133        }
134        Language::Portuguese => {
135            "--type e --description são obrigatórios ao criar uma nova memória".to_string()
136        }
137    }
138}
139
140/// `--target` only applies to re-embed operation.
141pub fn reembed_target_only(target: &str) -> String {
142    match current() {
143        Language::English => {
144            format!("--target {target} only applies to --operation re-embed")
145        }
146        Language::Portuguese => {
147            format!("--target {target} só se aplica a --operation re-embed")
148        }
149    }
150}
151
152/// Refusing orphan entity delete without --yes.
153pub fn refuse_delete_orphans_without_yes(orphan_count: usize) -> String {
154    match current() {
155        Language::English => format!(
156            "refusing to delete {orphan_count} orphan entities without --yes (use --dry-run to preview)"
157        ),
158        Language::Portuguese => format!(
159            "recusando excluir {orphan_count} entidades órfãs sem --yes (use --dry-run para pré-visualizar)"
160        ),
161    }
162}
163
164/// Refusing vec orphan delete without --yes.
165pub fn refuse_delete_vec_orphans_without_yes(
166    orphan_count: i64,
167    orphan_entities_count: i64,
168    orphan_chunks_count: i64,
169) -> String {
170    match current() {
171        Language::English => format!(
172            "refusing to delete {orphan_count} memory embedding + {orphan_entities_count} vec_entities + {orphan_chunks_count} vec_chunks orphan rows without --yes (use --dry-run to preview)"
173        ),
174        Language::Portuguese => format!(
175            "recusando excluir {orphan_count} embeddings de memória + {orphan_entities_count} vec_entities + {orphan_chunks_count} vec_chunks órfãos sem --yes (use --dry-run para pré-visualizar)"
176        ),
177    }
178}
179
180/// Refusing to release slot without --yes.
181pub fn refuse_release_slot_without_yes(slot_id: &str, path: &str) -> String {
182    match current() {
183        Language::English => {
184            format!("refusing to release slot {slot_id} without --yes (file: {path})")
185        }
186        Language::Portuguese => {
187            format!("recusando liberar slot {slot_id} sem --yes (arquivo: {path})")
188        }
189    }
190}
191
192/// Self-referential merge by id (generic).
193pub fn self_merge_id(id: i64, target_id: i64) -> String {
194    match current() {
195        Language::English => format!(
196            "source entity id={id} equals target id={target_id} — \
197             self-referential merge is not allowed"
198        ),
199        Language::Portuguese => format!(
200            "entidade fonte id={id} é igual ao alvo id={target_id} — \
201             merge auto-referencial não é permitido"
202        ),
203    }
204}
205
206/// Self-referential merge by id (pre-check with --ids hint).
207pub fn self_merge_id_in_ids(id: i64) -> String {
208    match current() {
209        Language::English => format!(
210            "source entity id={id} equals target id={id} — \
211             self-referential merge is not allowed (remove target from --ids)"
212        ),
213        Language::Portuguese => format!(
214            "entidade fonte id={id} é igual ao alvo id={id} — \
215             merge auto-referencial não é permitido (remova o alvo de --ids)"
216        ),
217    }
218}
219
220/// Self-referential merge by name (generic).
221pub fn self_merge_name(name: &str, target_name: &str) -> String {
222    match current() {
223        Language::English => format!(
224            "source entity '{name}' equals target '{target_name}' — \
225             self-referential merge is not allowed"
226        ),
227        Language::Portuguese => format!(
228            "entidade fonte '{name}' é igual ao alvo '{target_name}' — \
229             merge auto-referencial não é permitido"
230        ),
231    }
232}
233
234/// Self-referential merge by name (pre-check with --names hint).
235pub fn self_merge_name_in_names(name: &str) -> String {
236    match current() {
237        Language::English => format!(
238            "source entity '{name}' equals target '{name}' — \
239             self-referential merge is not allowed (remove target from --names)"
240        ),
241        Language::Portuguese => format!(
242            "entidade fonte '{name}' é igual ao alvo '{name}' — \
243             merge auto-referencial não é permitido (remova o alvo de --names)"
244        ),
245    }
246}
247
248/// Source name resolves to target id (self-merge).
249pub fn self_merge_name_resolves_to_target(name: &str, target_id: i64) -> String {
250    match current() {
251        Language::English => format!(
252            "source entity '{name}' resolves to the target (id={target_id}) — \
253             self-referential merge is not allowed"
254        ),
255        Language::Portuguese => format!(
256            "entidade fonte '{name}' resolve para o alvo (id={target_id}) — \
257             merge auto-referencial não é permitido"
258        ),
259    }
260}
261
262/// Source and target entity names identical.
263pub fn source_target_entity_names_identical() -> String {
264    match current() {
265        Language::English => "source and target entity names are identical".to_string(),
266        Language::Portuguese => "nomes de entidade de origem e destino são idênticos".to_string(),
267    }
268}
269
270/// Source and target names identical.
271pub fn source_target_names_identical() -> String {
272    match current() {
273        Language::English => "source and target names are identical".to_string(),
274        Language::Portuguese => "nomes de origem e destino são idênticos".to_string(),
275    }
276}
277
278/// GAP-SG-142: `--filter` expression has an empty key on the left-hand side.
279pub fn agent_surface_filter_empty_key(expr: &str) -> String {
280    match current() {
281        Language::English => {
282            format!("invalid --filter expression '{expr}': the key must not be empty")
283        }
284        Language::Portuguese => {
285            format!("expressão --filter inválida '{expr}': a chave não pode ser vazia")
286        }
287    }
288}
289
290/// GAP-SG-142: `--filter` expression carries no recognised operator.
291pub fn agent_surface_filter_invalid(expr: &str) -> String {
292    match current() {
293        Language::English => format!(
294            "invalid --filter expression '{expr}': expected key=value, key!=value or key~substring"
295        ),
296        Language::Portuguese => format!(
297            "expressão --filter inválida '{expr}': esperado chave=valor, chave!=valor ou chave~substring"
298        ),
299    }
300}
301
302/// Unknown pending_embeddings status string.
303pub fn unknown_pending_embeddings_status(other: &str) -> String {
304    match current() {
305        Language::English => format!("unknown pending_embeddings status: {other}"),
306        Language::Portuguese => {
307            format!("status de pending_embeddings desconhecido: {other}")
308        }
309    }
310}
311
312/// Unknown pending_memories status string.
313pub fn unknown_pending_memories_status(other: &str) -> String {
314    match current() {
315        Language::English => format!("unknown pending_memories status: {other}"),
316        Language::Portuguese => {
317            format!("status de pending_memories desconhecido: {other}")
318        }
319    }
320}
321
322/// Generic "missing / required field" helper.
323pub fn missing_field(field: &str) -> String {
324    match current() {
325        Language::English => format!("missing required field: {field}"),
326        Language::Portuguese => format!("campo obrigatório ausente: {field}"),
327    }
328}
329
330/// Sub-queries file has no usable lines.
331pub fn sub_queries_file_empty(path: &str) -> String {
332    match current() {
333        Language::English => format!("sub-queries file '{path}' has no usable lines"),
334        Language::Portuguese => {
335            format!("arquivo de sub-consultas '{path}' não tem linhas utilizáveis")
336        }
337    }
338}
339
340/// Failed to read names file.
341pub fn failed_to_read_names_file(path: &str, err: &impl std::fmt::Display) -> String {
342    match current() {
343        Language::English => format!("failed to read names file {path}: {err}"),
344        Language::Portuguese => format!("falha ao ler arquivo de nomes {path}: {err}"),
345    }
346}
347
348/// Localized message for `sync_destination_equals_source`.
349pub fn sync_destination_equals_source() -> String {
350    match current() {
351        Language::English => {
352            "destination path must differ from the source database path".to_string()
353        }
354        Language::Portuguese => {
355            "caminho de destino deve ser diferente do caminho do banco de dados fonte".to_string()
356        }
357    }
358}
359
360/// `schema --name <ID>` was given an id that is not shipped in `docs/schemas/`.
361///
362/// `suggestion` carries the nearest known id when one is close enough; the
363/// message stays terse when nothing is, because an unrelated pointer is worse
364/// than none.
365pub fn unknown_schema_id(id: &str, suggestion: Option<&str>) -> String {
366    match (current(), suggestion) {
367        (Language::English, Some(near)) => {
368            format!("unknown schema id '{id}'; did you mean '{near}'? Run `sqlite-graphrag schema` to list every id")
369        }
370        (Language::English, None) => {
371            format!("unknown schema id '{id}'; run `sqlite-graphrag schema` to list every id")
372        }
373        (Language::Portuguese, Some(near)) => {
374            format!("id de schema desconhecido '{id}'; você quis dizer '{near}'? Execute `sqlite-graphrag schema` para listar todos os ids")
375        }
376        (Language::Portuguese, None) => {
377            format!("id de schema desconhecido '{id}'; execute `sqlite-graphrag schema` para listar todos os ids")
378        }
379    }
380}
381
382/// A stdin-reading path was requested while `--no-input` was in force.
383///
384/// Reported before the read is attempted, so the guarantee holds even when a
385/// pipe is present and would have supplied data.
386pub fn no_input_blocks_stdin() -> String {
387    match current() {
388        Language::English => "--no-input is in force: this invocation refuses to read stdin; \
389             supply the content through a file or inline flag \
390             (for example --body / --body-file), or drop --no-input"
391            .to_string(),
392        Language::Portuguese => "--no-input está em vigor: esta invocação recusa ler stdin; \
393             forneça o conteúdo por arquivo ou flag inline \
394             (por exemplo --body / --body-file), ou remova --no-input"
395            .to_string(),
396    }
397}