Skip to main content

sqlite_graphrag/i18n/validation/
messages_a.rs

1use crate::i18n::{current, Language};
2
3/// Localized message for `name_length`.
4pub fn name_length(max: usize) -> String {
5    match current() {
6        Language::English => format!("name must be 1-{max} chars"),
7        Language::Portuguese => format!("nome deve ter entre 1 e {max} caracteres"),
8    }
9}
10
11/// Localized message for `reserved_name`.
12pub fn reserved_name() -> String {
13    match current() {
14        Language::English => {
15            "names and namespaces starting with __ are reserved for internal use".to_string()
16        }
17        Language::Portuguese => {
18            "nomes e namespaces iniciados com __ são reservados para uso interno".to_string()
19        }
20    }
21}
22
23/// Localized message for `name_kebab`.
24pub fn name_kebab(nome: &str) -> String {
25    match current() {
26        Language::English => {
27            format!("name must be kebab-case slug (lowercase letters, digits, hyphens): '{nome}'")
28        }
29        Language::Portuguese => {
30            format!("nome deve estar em kebab-case (minúsculas, dígitos, hífens): '{nome}'")
31        }
32    }
33}
34
35/// Localized message for `description_exceeds`.
36pub fn description_exceeds(max: usize) -> String {
37    match current() {
38        Language::English => format!("description must be <= {max} chars"),
39        Language::Portuguese => format!("descrição deve ter no máximo {max} caracteres"),
40    }
41}
42
43/// Localized message for `body_exceeds`.
44pub fn body_exceeds(max: usize) -> String {
45    match current() {
46        Language::English => format!("body exceeds {max} bytes"),
47        Language::Portuguese => format!("corpo excede {max} bytes"),
48    }
49}
50
51/// Localized message for `new_name_length`.
52pub fn new_name_length(max: usize) -> String {
53    match current() {
54        Language::English => format!("new-name must be 1-{max} chars"),
55        Language::Portuguese => format!("novo nome deve ter entre 1 e {max} caracteres"),
56    }
57}
58
59/// Localized message for `new_name_kebab`.
60pub fn new_name_kebab(nome: &str) -> String {
61    match current() {
62        Language::English => format!(
63            "new-name must be kebab-case slug (lowercase letters, digits, hyphens): '{nome}'"
64        ),
65        Language::Portuguese => {
66            format!("novo nome deve estar em kebab-case (minúsculas, dígitos, hífens): '{nome}'")
67        }
68    }
69}
70
71/// Localized message for `namespace_length`.
72pub fn namespace_length() -> String {
73    match current() {
74        Language::English => "namespace must be 1-80 chars".to_string(),
75        Language::Portuguese => "namespace deve ter entre 1 e 80 caracteres".to_string(),
76    }
77}
78
79/// Localized message for `namespace_format`.
80pub fn namespace_format() -> String {
81    match current() {
82        Language::English => "namespace must be alphanumeric + hyphens/underscores".to_string(),
83        Language::Portuguese => {
84            "namespace deve ser alfanumérico com hífens/sublinhados".to_string()
85        }
86    }
87}
88
89/// Localized message for `path_traversal`.
90pub fn path_traversal(p: &str) -> String {
91    match current() {
92        Language::English => format!("path traversal rejected: {p}"),
93        Language::Portuguese => format!("traversal de caminho rejeitado: {p}"),
94    }
95}
96
97/// Rejects a `config set` key that is not in the canonical registry.
98///
99/// `suggestion` carries the nearest known key when one is similar enough,
100/// so a typo is actionable without the operator listing every key.
101pub fn config_key_unknown(key: &str, suggestion: Option<&str>) -> String {
102    match (current(), suggestion) {
103        (Language::English, Some(s)) => format!(
104            "unknown config key '{key}'; did you mean '{s}'? \
105             list valid keys with `config doctor --json`"
106        ),
107        (Language::English, None) => format!(
108            "unknown config key '{key}'; \
109             list valid keys with `config doctor --json`"
110        ),
111        (Language::Portuguese, Some(s)) => format!(
112            "chave de config desconhecida '{key}'; você quis dizer '{s}'? \
113             liste as chaves válidas com `config doctor --json`"
114        ),
115        (Language::Portuguese, None) => format!(
116            "chave de config desconhecida '{key}'; \
117             liste as chaves válidas com `config doctor --json`"
118        ),
119    }
120}
121
122/// Rejects a `config set` key that was advertised historically but never read.
123///
124/// Distinct from [`config_key_unknown`] because the operator followed the
125/// old documentation rather than mistyping, so the message names the
126/// replacement directly instead of guessing.
127pub fn config_key_retired(key: &str, replacement: &str) -> String {
128    match current() {
129        Language::English => format!(
130            "config key '{key}' was never read by this binary; \
131             use '{replacement}' instead"
132        ),
133        Language::Portuguese => format!(
134            "a chave de config '{key}' nunca foi lida por este binário; \
135             use '{replacement}' no lugar"
136        ),
137    }
138}
139
140/// Localized message for `invalid_tz`.
141pub fn invalid_tz(v: &str) -> String {
142    match current() {
143        Language::English => {
144            format!("display.tz invalid: '{v}'; use an IANA name like 'America/Sao_Paulo'")
145        }
146        Language::Portuguese => {
147            format!("display.tz inválido: '{v}'; use um nome IANA como 'America/Sao_Paulo'")
148        }
149    }
150}
151
152/// Localized message for `empty_query`.
153pub fn empty_query() -> String {
154    match current() {
155        Language::English => "query cannot be empty".to_string(),
156        Language::Portuguese => "a consulta não pode estar vazia".to_string(),
157    }
158}
159
160/// Localized message for `empty_body`.
161pub fn empty_body() -> String {
162    match current() {
163        Language::English => "body cannot be empty: provide --body, --body-file, or --body-stdin with content, or supply a graph via --entities-file/--graph-stdin".to_string(),
164        Language::Portuguese => "o corpo não pode estar vazio: forneça --body, --body-file ou --body-stdin com conteúdo, ou um grafo via --entities-file/--graph-stdin".to_string(),
165    }
166}
167
168/// Localized message for `invalid_namespace_config`.
169pub fn invalid_namespace_config(path: &str, err: &str) -> String {
170    match current() {
171        Language::English => {
172            format!("invalid project namespace config '{path}': {err}")
173        }
174        Language::Portuguese => {
175            format!("configuração de namespace de projeto inválida '{path}': {err}")
176        }
177    }
178}
179
180/// Localized message for `invalid_projects_mapping`.
181pub fn invalid_projects_mapping(path: &str, err: &str) -> String {
182    match current() {
183        Language::English => format!("invalid projects mapping '{path}': {err}"),
184        Language::Portuguese => format!("mapeamento de projetos inválido '{path}': {err}"),
185    }
186}
187
188/// Localized message for `self_referential_link`.
189pub fn self_referential_link() -> String {
190    match current() {
191        Language::English => "--from and --to must be different entities — self-referential relationships are not supported".to_string(),
192        Language::Portuguese => "--from e --to devem ser entidades diferentes — relacionamentos auto-referenciais não são suportados".to_string(),
193    }
194}
195
196/// Localized message for `invalid_link_weight`.
197pub fn invalid_link_weight(weight: f64) -> String {
198    match current() {
199        Language::English => {
200            format!("--weight: must be between 0.0 and 1.0 (actual: {weight})")
201        }
202        Language::Portuguese => {
203            format!("--weight: deve estar entre 0.0 e 1.0 (atual: {weight})")
204        }
205    }
206}
207
208/// Localized message for `sync_destination_equals_source`.
209pub fn sync_destination_equals_source() -> String {
210    match current() {
211        Language::English => {
212            "destination path must differ from the source database path".to_string()
213        }
214        Language::Portuguese => {
215            "caminho de destino deve ser diferente do caminho do banco de dados fonte".to_string()
216        }
217    }
218}
219
220// ── R-I18N-01 onda 1: reusable Validation catalog helpers ──────────────
221// Prefer these over ad-hoc `AppError::Validation(format!(...))` so EN/PT
222// stay in one place. English is the default agent language; PT follows
223// `current()` when locale is Portuguese.
224
225/// Generic "missing / required field" helper.
226pub fn missing_field(field: &str) -> String {
227    match current() {
228        Language::English => format!("missing required field: {field}"),
229        Language::Portuguese => format!("campo obrigatório ausente: {field}"),
230    }
231}
232
233/// Directory path does not exist.
234pub fn directory_not_found(path: &str) -> String {
235    match current() {
236        Language::English => format!("directory not found: {path}"),
237        Language::Portuguese => format!("diretório não encontrado: {path}"),
238    }
239}
240
241/// Path exists but is not a directory.
242pub fn not_a_directory(path: &str) -> String {
243    match current() {
244        Language::English => format!("path is not a directory: {path}"),
245        Language::Portuguese => format!("caminho não é um diretório: {path}"),
246    }
247}
248
249/// `--max-files` cap exceeded (generic form used by ingest-claude/codex).
250pub fn max_files_exceeded(found: usize, max: usize) -> String {
251    match current() {
252        Language::English => {
253            format!("found {found} files, exceeds --max-files cap of {max}")
254        }
255        Language::Portuguese => {
256            format!("encontrados {found} arquivos, excede o limite --max-files de {max}")
257        }
258    }
259}
260
261/// `--max-files` cap exceeded with pattern-matching wording (ingest).
262pub fn max_files_exceeded_matching(found: usize, max: usize) -> String {
263    match current() {
264        Language::English => format!(
265            "found {found} files matching pattern, exceeds --max-files cap of {max} \
266             (raise the cap or narrow the pattern)"
267        ),
268        Language::Portuguese => format!(
269            "encontrados {found} arquivos no padrão, excede o limite --max-files de {max} \
270             (aumente o limite ou restrinja o padrão)"
271        ),
272    }
273}
274
275/// `--max-files` cap exceeded with all-or-nothing abort (ingest-opencode).
276pub fn max_files_exceeded_all_or_nothing(found: usize, max: usize) -> String {
277    match current() {
278        Language::English => format!(
279            "found {found} files exceeding --max-files cap of {max}; aborting (all-or-nothing)"
280        ),
281        Language::Portuguese => format!(
282            "encontrados {found} arquivos excedendo o limite --max-files de {max}; \
283             abortando (tudo-ou-nada)"
284        ),
285    }
286}
287
288/// File bytes are not valid UTF-8.
289pub fn file_not_utf8(err: &impl std::fmt::Display) -> String {
290    match current() {
291        Language::English => format!("file is not valid UTF-8: {err}"),
292        Language::Portuguese => format!("arquivo não é UTF-8 válido: {err}"),
293    }
294}
295
296/// Queue resume (`status=processing` → `pending`) failed.
297pub fn queue_resume_failed(err: &impl std::fmt::Display) -> String {
298    match current() {
299        Language::English => format!("queue resume failed: {err}"),
300        Language::Portuguese => format!("falha ao retomar a fila: {err}"),
301    }
302}
303
304/// Queue retry-failed reset failed.
305pub fn queue_retry_failed_reset_failed(err: &impl std::fmt::Display) -> String {
306    match current() {
307        Language::English => format!("queue retry-failed reset failed: {err}"),
308        Language::Portuguese => {
309            format!("falha ao redefinir itens com falha da fila: {err}")
310        }
311    }
312}
313
314/// Queue clear failed.
315pub fn queue_clear_failed(err: &impl std::fmt::Display) -> String {
316    match current() {
317        Language::English => format!("queue clear failed: {err}"),
318        Language::Portuguese => format!("falha ao limpar a fila: {err}"),
319    }
320}
321
322/// Queue insert failed.
323pub fn queue_insert_failed(err: &impl std::fmt::Display) -> String {
324    match current() {
325        Language::English => format!("queue insert failed: {err}"),
326        Language::Portuguese => format!("falha ao inserir na fila: {err}"),
327    }
328}
329
330/// Invalid JSON for a CLI flag that takes a file path (e.g. `--entities-file`).
331pub fn invalid_json_in_flag(flag: &str, err: &impl std::fmt::Display) -> String {
332    match current() {
333        Language::English => format!("invalid JSON in {flag}: {err}"),
334        Language::Portuguese => format!("JSON inválido em {flag}: {err}"),
335    }
336}
337
338/// Invalid JSON payload on a stdin flag (e.g. `--graph-stdin`).
339pub fn invalid_json_payload_on_flag(flag: &str, err: &impl std::fmt::Display) -> String {
340    match current() {
341        Language::English => format!("invalid JSON payload on {flag}: {err}"),
342        Language::Portuguese => format!("payload JSON inválido em {flag}: {err}"),
343    }
344}
345
346/// Relation format error scoped to a source→target edge.
347pub fn relation_format_for_relationship(
348    err: &impl std::fmt::Display,
349    source: &str,
350    target: &str,
351) -> String {
352    match current() {
353        Language::English => format!("{err} for relationship '{source}' -> '{target}'"),
354        Language::Portuguese => {
355            format!("{err} para relacionamento '{source}' -> '{target}'")
356        }
357    }
358}
359
360/// Relationship strength outside [0.0, 1.0].
361pub fn invalid_relationship_strength(strength: f64, source: &str, target: &str) -> String {
362    match current() {
363        Language::English => format!(
364            "invalid strength {strength} for relationship '{source}' -> '{target}'; \
365             expected value in [0.0, 1.0]"
366        ),
367        Language::Portuguese => format!(
368            "força inválida {strength} para relacionamento '{source}' -> '{target}'; \
369             valor esperado em [0.0, 1.0]"
370        ),
371    }
372}
373
374/// Ingest aborted on first failure (`--fail-fast` / default all-or-nothing path).
375pub fn ingest_aborted_on_first_failure(err: &impl std::fmt::Display) -> String {
376    match current() {
377        Language::English => format!("ingest aborted on first failure: {err}"),
378        Language::Portuguese => format!("ingest abortado na primeira falha: {err}"),
379    }
380}
381
382/// Name normalized to empty (only hyphens/underscores/spaces).
383pub fn name_empty_after_normalization() -> String {
384    match current() {
385        Language::English => {
386            "name cannot be empty after normalization (input was blank or contained only hyphens/underscores/spaces)"
387                .to_string()
388        }
389        Language::Portuguese => {
390            "nome não pode ficar vazio após normalização (entrada em branco ou só com hífens/sublinhados/espaços)"
391                .to_string()
392        }
393    }
394}
395
396/// `--strict-name` refused auto-normalization.
397pub fn strict_name_not_canonical(original: &str, normalized: &str) -> String {
398    match current() {
399        Language::English => format!(
400            "--strict-name is set but '{original}' is not canonical kebab-case; \
401             re-run with --name '{normalized}' (or drop --strict-name to allow auto-normalization)"
402        ),
403        Language::Portuguese => format!(
404            "--strict-name está ativo mas '{original}' não é kebab-case canônico; \
405             reexecute com --name '{normalized}' (ou remova --strict-name para permitir auto-normalização)"
406        ),
407    }
408}
409
410/// `--enable-ner` and `--skip-extraction` conflict.
411pub fn enable_ner_skip_extraction_exclusive() -> String {
412    match current() {
413        Language::English => {
414            "--enable-ner and --skip-extraction are mutually exclusive; remove one".to_string()
415        }
416        Language::Portuguese => {
417            "--enable-ner e --skip-extraction são mutuamente exclusivos; remova um".to_string()
418        }
419    }
420}
421
422/// CREATE path requires `--type` and `--description`.
423pub fn type_and_description_required() -> String {
424    match current() {
425        Language::English => {
426            "--type and --description are required when creating a new memory".to_string()
427        }
428        Language::Portuguese => {
429            "--type e --description são obrigatórios ao criar uma nova memória".to_string()
430        }
431    }
432}
433
434/// External CLI binary missing at an explicit path.
435pub fn binary_not_found_at_path(tool: &str, path: &str) -> String {
436    match current() {
437        Language::English => format!("{tool} binary not found at explicit path: {path}"),
438        Language::Portuguese => {
439            format!("binário {tool} não encontrado no caminho explícito: {path}")
440        }
441    }
442}
443
444/// Executable not found in PATH.
445pub fn executable_not_in_path(binary: &str) -> String {
446    match current() {
447        Language::English => format!(
448            "executable '{binary}' not found in PATH; ensure Codex CLI is installed"
449        ),
450        Language::Portuguese => format!(
451            "executável '{binary}' não encontrado no PATH; certifique-se de que o Codex CLI está instalado"
452        ),
453    }
454}
455
456/// Tool version below the minimum required.
457pub fn version_below_minimum(tool: &str, actual: &str, min: &str) -> String {
458    match current() {
459        Language::English => {
460            format!("{tool} version {actual} is below minimum required {min}")
461        }
462        Language::Portuguese => {
463            format!("versão {actual} de {tool} está abaixo do mínimo exigido {min}")
464        }
465    }
466}
467
468/// External process timed out.
469pub fn process_timed_out(cmd: &str, secs: u64) -> String {
470    match current() {
471        Language::English => format!("{cmd} timed out after {secs} seconds"),
472        Language::Portuguese => format!("{cmd} expirou após {secs} segundos"),
473    }
474}
475
476/// External process exited with a non-zero code.
477pub fn process_exited(cmd: &str, code: Option<i32>, stderr: &str) -> String {
478    match current() {
479        Language::English => format!("{cmd} exited with code {code:?}: {stderr}"),
480        Language::Portuguese => {
481            format!("{cmd} encerrou com código {code:?}: {stderr}")
482        }
483    }
484}
485
486/// Failed to build the HTTP client.
487pub fn http_client_build_failed(err: &impl std::fmt::Display) -> String {
488    match current() {
489        Language::English => format!("failed to build HTTP client: {err}"),
490        Language::Portuguese => format!("falha ao construir cliente HTTP: {err}"),
491    }
492}
493
494/// Invalid JSON schema for an OpenRouter request body.
495pub fn invalid_json_schema_for_request(err: &impl std::fmt::Display) -> String {
496    match current() {
497        Language::English => {
498            format!("invalid JSON schema for OpenRouter request: {err}")
499        }
500        Language::Portuguese => {
501            format!("schema JSON inválido para requisição OpenRouter: {err}")
502        }
503    }
504}
505
506/// Model returned no structured content.
507pub fn model_no_structured_content(model: &str) -> String {
508    match current() {
509        Language::English => format!(
510            "model '{model}' returned no structured content (incompatible with \
511             structured outputs, or refused the request)"
512        ),
513        Language::Portuguese => format!(
514            "modelo '{model}' não retornou conteúdo estruturado (incompatível com \
515             saídas estruturadas, ou recusou a requisição)"
516        ),
517    }
518}
519
520/// Model content could not be parsed even after JSON repair.
521pub fn model_json_parse_failed(model: &str, err: &impl std::fmt::Display) -> String {
522    match current() {
523        Language::English => format!(
524            "model '{model}' returned content that could not be parsed even after \
525             JSON repair: {err}"
526        ),
527        Language::Portuguese => format!(
528            "modelo '{model}' retornou conteúdo que não pôde ser parseado mesmo após \
529             reparo de JSON: {err}"
530        ),
531    }
532}
533
534/// Model returned non-object JSON after repair.
535pub fn model_non_object_json(model: &str, shape: &str) -> String {
536    match current() {
537        Language::English => format!(
538            "model '{model}' returned non-object JSON after repair (got {shape}); \
539             likely a refusal or malformed structured output"
540        ),
541        Language::Portuguese => format!(
542            "modelo '{model}' retornou JSON não-objeto após reparo (obteve {shape}); \
543             provavelmente uma recusa ou saída estruturada malformada"
544        ),
545    }
546}
547
548/// HTTP request failed (transport-level).
549pub fn http_request_failed(err: &impl std::fmt::Display) -> String {
550    match current() {
551        Language::English => format!("HTTP request failed: {err}"),
552        Language::Portuguese => format!("requisição HTTP falhou: {err}"),
553    }
554}
555
556/// Failed to read HTTP response body.
557pub fn failed_to_read_response_body(err: &impl std::fmt::Display) -> String {
558    match current() {
559        Language::English => format!("failed to read response body: {err}"),
560        Language::Portuguese => format!("falha ao ler corpo da resposta: {err}"),
561    }
562}
563
564/// Failed to parse chat response JSON.
565pub fn failed_to_parse_chat_response(err: &impl std::fmt::Display) -> String {
566    match current() {
567        Language::English => format!("failed to parse chat response: {err}"),
568        Language::Portuguese => format!("falha ao parsear resposta de chat: {err}"),
569    }
570}
571
572/// OpenRouter returned a non-success status for a model.
573pub fn openrouter_status_error(status: &impl std::fmt::Display, model: &str, body: &str) -> String {
574    match current() {
575        Language::English => {
576            format!("OpenRouter returned {status} for model '{model}': {body}")
577        }
578        Language::Portuguese => {
579            format!("OpenRouter retornou {status} para o modelo '{model}': {body}")
580        }
581    }
582}
583
584/// OpenRouter 5xx server error.
585pub fn openrouter_server_error(status: &impl std::fmt::Display) -> String {
586    match current() {
587        Language::English => format!("OpenRouter server error: {status}"),
588        Language::Portuguese => format!("erro de servidor OpenRouter: {status}"),
589    }
590}
591
592/// Unexpected HTTP status with body snippet.
593pub fn unexpected_http_status(status: &impl std::fmt::Display, body: &str) -> String {
594    match current() {
595        Language::English => format!("unexpected HTTP {status}: {body}"),
596        Language::Portuguese => format!("HTTP inesperado {status}: {body}"),
597    }
598}
599
600/// `--target` only applies to re-embed operation.
601pub fn reembed_target_only(target: &str) -> String {
602    match current() {
603        Language::English => {
604            format!("--target {target} only applies to --operation re-embed")
605        }
606        Language::Portuguese => {
607            format!("--target {target} só se aplica a --operation re-embed")
608        }
609    }
610}
611
612/// System load average exceeds 2× ncpus.
613pub fn system_load_exceeded(load: f64, ncpus: usize) -> String {
614    match current() {
615        Language::English => format!(
616            "system load average {load:.2} exceeds 2x ncpus ({ncpus}); \
617             pass --no-max-load-check to override (not recommended)"
618        ),
619        Language::Portuguese => format!(
620            "carga média do sistema {load:.2} excede 2x ncpus ({ncpus}); \
621             passe --no-max-load-check para sobrescrever (não recomendado)"
622        ),
623    }
624}
625
626/// Preflight rate-limit with a distinct fallback provider.
627pub fn preflight_rate_limit_fallback(mode: &str, reason: &str, fallback: &str) -> String {
628    match current() {
629        Language::English => format!(
630            "preflight detected rate limit on {mode}: {reason}; \
631             re-invoke with `--mode {fallback}` to use the fallback provider"
632        ),
633        Language::Portuguese => format!(
634            "preflight detectou limite de taxa em {mode}: {reason}; \
635             reexecute com `--mode {fallback}` para usar o provedor de fallback"
636        ),
637    }
638}
639
640/// Preflight rate-limit when fallback equals current mode.
641pub fn preflight_rate_limit_same_mode(mode: &str, reason: &str) -> String {
642    match current() {
643        Language::English => format!(
644            "preflight detected rate limit on {mode}: {reason}; \
645             --fallback-mode matches --mode, no recovery possible"
646        ),
647        Language::Portuguese => format!(
648            "preflight detectou limite de taxa em {mode}: {reason}; \
649             --fallback-mode coincide com --mode, sem recuperação possível"
650        ),
651    }
652}
653
654/// Preflight rate-limit with operator suggestion (no fallback configured).
655pub fn preflight_rate_limit_suggestion(mode: &str, reason: &str, suggestion: &str) -> String {
656    match current() {
657        Language::English => format!(
658            "preflight detected rate limit on {mode}: {reason}; \
659             {suggestion}; pass --fallback-mode codex to recover"
660        ),
661        Language::Portuguese => format!(
662            "preflight detectou limite de taxa em {mode}: {reason}; \
663             {suggestion}; passe --fallback-mode codex para recuperar"
664        ),
665    }
666}
667
668/// OpenRouter mode requires an explicit model flag.
669pub fn openrouter_model_required() -> String {
670    match current() {
671        Language::English => {
672            "--mode openrouter requires --openrouter-model (no default model is allowed)"
673                .to_string()
674        }
675        Language::Portuguese => {
676            "--mode openrouter exige --openrouter-model (nenhum modelo padrão é permitido)"
677                .to_string()
678        }
679    }
680}
681
682/// OpenRouter API key could not be resolved.
683pub fn openrouter_api_key_not_found() -> String {
684    match current() {
685        Language::English => "OpenRouter API key not found; store it via \
686             `config add-key --provider openrouter`, or pass --openrouter-api-key \
687             (product env is deprecated)"
688            .to_string(),
689        Language::Portuguese => "chave de API OpenRouter não encontrada; armazene via \
690             `config add-key --provider openrouter`, ou passe --openrouter-api-key \
691             (env de produto está depreciada)"
692            .to_string(),
693    }
694}