1#![allow(clippy::needless_pass_by_value)]
12
13use lean_rs_worker_parent::{
14 LeanWorkerDeclarationFlags, LeanWorkerDeclarationInspection as WorkerDeclarationInspection,
15 LeanWorkerDeclarationInspectionResult, LeanWorkerDeclarationProofSearchFacts, LeanWorkerDeclarationRow,
16 LeanWorkerDeclarationSearchFacts, LeanWorkerDeclarationSearchPruning, LeanWorkerDeclarationSearchResult,
17 LeanWorkerDeclarationSearchRow, LeanWorkerDeclarationSearchTimings, LeanWorkerDeclarationTargetInfo,
18 LeanWorkerDeclarationVerificationFacts, LeanWorkerDeclarationVerificationResult,
19 LeanWorkerDeclarationVerificationStatus, LeanWorkerDiagnostic, LeanWorkerElabFailure, LeanWorkerElabResult,
20 LeanWorkerKernelResult, LeanWorkerKernelStatus, LeanWorkerMetaResult, LeanWorkerModuleSourceSpan,
21 LeanWorkerProofAttemptEnvelope, LeanWorkerProofAttemptResult, LeanWorkerProofAttemptRow,
22 LeanWorkerProofAttemptStatus, LeanWorkerRendered, LeanWorkerRenderedInfo, LeanWorkerRendering,
23 LeanWorkerSourceRange,
24};
25
26#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
28pub struct SourceRange {
29 pub file: String,
30 pub start_line: u32,
31 pub start_column: u32,
32 pub end_line: u32,
33 pub end_column: u32,
34}
35
36#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
37pub struct Position {
38 pub line: u32,
39 pub column: u32,
40 pub end_line: Option<u32>,
41 pub end_column: Option<u32>,
42}
43
44#[derive(Debug, Clone, Copy, serde::Serialize, schemars::JsonSchema)]
48#[serde(rename_all = "snake_case")]
49pub enum Severity {
50 Error,
51 Warning,
52 Info,
53}
54
55impl Severity {
56 fn from_worker(s: &str) -> Self {
59 match s {
60 "error" => Self::Error,
61 "warning" => Self::Warning,
62 _ => Self::Info,
63 }
64 }
65}
66
67#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
68pub struct Diagnostic {
69 pub severity: Severity,
70 pub message: String,
71 pub position: Option<Position>,
72 #[serde(skip_serializing_if = "Option::is_none")]
76 pub file: Option<String>,
77}
78
79#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
83pub struct ElabFailure {
84 pub diagnostics: Vec<Diagnostic>,
85 pub truncated: bool,
86}
87
88#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
89pub struct ElabSuccess {
90 pub ok: bool,
91}
92
93#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
94pub struct KernelOutcome {
95 pub status: String,
97 pub summary: Option<KernelSummary>,
98 pub failure: Option<ElabFailure>,
99}
100
101#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
102pub struct KernelSummary {
103 pub declaration_name: String,
104 pub kind: String,
105 pub type_signature: String,
109}
110
111#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
112pub struct MetaOutcome {
113 pub status: String,
115 pub rendered: Option<String>,
116 pub definitionally_equal: Option<bool>,
117 pub failure: Option<ElabFailure>,
118 #[serde(skip)]
124 #[schemars(skip)]
125 pub raw_fallback_used: bool,
126}
127
128#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
129pub struct DeclarationRow {
130 pub name: String,
131 pub kind: String,
132 pub type_signature: Option<String>,
136 pub source: Option<SourceRange>,
137}
138
139#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
140pub struct RenderedText {
141 pub value: String,
142 pub truncated: bool,
143}
144
145#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
146pub struct ModuleSourceSpan {
147 pub start_line: u32,
148 pub start_column: u32,
149 pub end_line: u32,
150 pub end_column: u32,
151}
152
153#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
154pub struct ProofActionDeclarationTarget {
155 pub short_name: String,
156 pub declaration_name: String,
157 pub namespace_name: String,
158 pub declaration_kind: String,
159 pub declaration_span: ModuleSourceSpan,
160 pub name_span: ModuleSourceSpan,
161 pub body_span: ModuleSourceSpan,
162}
163
164#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
165pub struct DeclarationFlags {
166 pub is_private: bool,
167 pub is_generated: bool,
168 pub is_internal: bool,
169}
170
171#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
172pub struct DeclarationSummary {
173 pub name: String,
174 pub kind: String,
175 pub module: Option<String>,
176 pub source: Option<SourceRange>,
177 pub match_reason: String,
178 pub score: i32,
179 pub rank: usize,
180 pub flags: DeclarationFlags,
181}
182
183#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
184pub struct DeclarationSearchPruning {
185 pub stage: String,
186 pub reason: String,
187 pub count: usize,
188}
189
190#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
191pub struct DeclarationSearchTimings {
192 pub scan_micros: u64,
193 pub rank_micros: u64,
194 pub source_micros: u64,
195}
196
197#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
198pub struct DeclarationSearchFacts {
199 pub declarations_scanned: usize,
200 pub after_name_filter: usize,
201 pub after_kind_filter: usize,
202 pub after_required_constants_filter: usize,
203 pub after_conclusion_filter: usize,
204 pub after_scope_filter: usize,
205 pub source_lookups: usize,
206 pub broad_pruning: Vec<DeclarationSearchPruning>,
207 pub truncated: bool,
208 pub timings: DeclarationSearchTimings,
209}
210
211#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
212pub struct DeclarationSearchResult {
213 pub declarations: Vec<DeclarationSummary>,
214 pub truncated: bool,
215 pub facts: DeclarationSearchFacts,
216}
217
218#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
219#[allow(
220 clippy::struct_excessive_bools,
221 reason = "proof-search booleans mirror independent lean-rs wire facts"
222)]
223pub struct DeclarationProofSearchFacts {
224 pub is_simp: bool,
225 pub is_rw_candidate: bool,
226 pub is_instance: bool,
227 pub is_class: bool,
228 pub class_name: Option<String>,
229}
230
231#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
232pub struct DeclarationInspection {
233 pub name: String,
234 pub kind: String,
235 pub module: Option<String>,
236 pub source: Option<SourceRange>,
237 pub statement: Option<RenderedText>,
238 #[serde(skip_serializing_if = "Option::is_none")]
243 pub statement_rendering: Option<String>,
244 pub docstring: Option<RenderedText>,
245 pub attributes: Vec<String>,
246 pub proof_search: DeclarationProofSearchFacts,
247 pub flags: DeclarationFlags,
248}
249
250#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
251#[serde(tag = "status", rename_all = "snake_case")]
252pub enum DeclarationInspectionResult {
253 Found {
254 declaration: Box<DeclarationInspection>,
255 },
256 NotFound {
257 name: Option<String>,
258 },
259 NeedsBuild,
266 Unsupported,
267}
268
269#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
270pub struct ProofAttemptCandidate {
271 pub id: String,
272 pub status: String,
273 pub snippet: RenderedText,
274 pub diagnostics: ElabFailure,
275 pub downstream_diagnostics: ElabFailure,
276 pub goals: Vec<RenderedText>,
277 pub declaration: Option<ProofActionDeclarationTarget>,
278 pub proof_position: Option<ProofPositionSummary>,
279 pub output_truncated: bool,
280}
281
282#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
283pub struct ProofPositionSummary {
284 pub index: u32,
285 pub tactic: RenderedText,
286}
287
288#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
289pub struct ProofAttemptEnvelope {
290 pub candidates: Vec<ProofAttemptCandidate>,
291 pub candidate_limit: u32,
292 pub candidates_truncated: bool,
293}
294
295#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
296#[serde(tag = "status", rename_all = "snake_case")]
297pub enum ProofAttemptResult {
298 Ok {
299 result: ProofAttemptEnvelope,
300 imports: Vec<String>,
301 },
302 MissingImports {
303 result: ProofAttemptEnvelope,
304 imports: Vec<String>,
305 missing: Vec<String>,
306 },
307 HeaderParseFailed {
308 diagnostics: ElabFailure,
309 },
310 Unsupported,
311}
312
313#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
314#[allow(
315 clippy::struct_excessive_bools,
316 reason = "verification booleans mirror independent lean-rs wire facts for proof policy decisions"
317)]
318pub struct DeclarationVerificationFacts {
319 pub target: Option<ProofActionDeclarationTarget>,
320 pub diagnostics: ElabFailure,
321 pub unresolved_goals: Vec<RenderedText>,
322 pub contains_sorry: bool,
323 pub contains_admit: bool,
324 pub contains_sorry_ax: bool,
325 pub axioms: Vec<String>,
326 pub axioms_truncated: bool,
327 pub axioms_available: bool,
332 pub output_truncated: bool,
333 #[serde(skip_serializing_if = "Vec::is_empty")]
336 pub candidates: Vec<ProofActionDeclarationTarget>,
337 pub facts_trustworthy: bool,
344}
345
346#[derive(Debug, Clone, serde::Serialize, schemars::JsonSchema)]
347#[serde(tag = "status", rename_all = "snake_case")]
348pub enum DeclarationVerificationResult {
349 Ok {
350 verification_status: String,
351 facts: Box<DeclarationVerificationFacts>,
352 imports: Vec<String>,
353 },
354 MissingImports {
355 verification_status: String,
356 facts: Box<DeclarationVerificationFacts>,
357 imports: Vec<String>,
358 missing: Vec<String>,
359 },
360 HeaderParseFailed {
361 diagnostics: ElabFailure,
362 },
363 Unsupported,
364}
365
366pub fn project_diagnostic(d: &LeanWorkerDiagnostic) -> Diagnostic {
369 let position = d.line.map(|line| Position {
370 line,
371 column: d.column.unwrap_or(0),
372 end_line: d.end_line,
373 end_column: d.end_column,
374 });
375 Diagnostic {
376 severity: Severity::from_worker(&d.severity),
377 message: d.message.clone(),
378 position,
379 file: meaningful_file_label(&d.file_label),
380 }
381}
382
383pub fn project_failure(failure: &LeanWorkerElabFailure) -> ElabFailure {
384 ElabFailure {
385 diagnostics: failure.diagnostics.iter().map(project_diagnostic).collect(),
386 truncated: failure.truncated,
387 }
388}
389
390pub(crate) fn project_source_range(range: LeanWorkerSourceRange) -> SourceRange {
391 SourceRange {
392 file: range.file,
393 start_line: range.start_line,
394 start_column: range.start_column,
395 end_line: range.end_line,
396 end_column: range.end_column,
397 }
398}
399
400fn meaningful_file_label(label: &str) -> Option<String> {
404 if label.is_empty() || label == "<elaborate>" || label.starts_with('<') {
405 None
406 } else {
407 Some(label.to_owned())
408 }
409}
410
411pub fn project_elab_result(result: LeanWorkerElabResult) -> std::result::Result<ElabSuccess, ElabFailure> {
417 if result.success {
418 Ok(ElabSuccess { ok: true })
419 } else {
420 Err(ElabFailure {
421 diagnostics: result.diagnostics.iter().map(project_diagnostic).collect(),
422 truncated: result.truncated,
423 })
424 }
425}
426
427pub fn project_kernel_result(result: LeanWorkerKernelResult) -> KernelOutcome {
428 let status = match result.status {
429 LeanWorkerKernelStatus::Checked => "Checked",
430 LeanWorkerKernelStatus::Rejected => "Rejected",
431 LeanWorkerKernelStatus::Unavailable => "Unavailable",
432 LeanWorkerKernelStatus::Unsupported => "Unsupported",
433 _ => "Unsupported",
434 };
435 let summary = result.summary.map(|s| KernelSummary {
436 declaration_name: s.declaration_name,
437 kind: s.kind,
438 type_signature: s.type_signature,
439 });
440 let failure = if matches!(result.status, LeanWorkerKernelStatus::Checked) {
441 None
442 } else {
443 Some(ElabFailure {
444 diagnostics: result.diagnostics.iter().map(project_diagnostic).collect(),
445 truncated: result.truncated,
446 })
447 };
448 KernelOutcome {
449 status: status.to_owned(),
450 summary,
451 failure,
452 }
453}
454
455pub fn project_meta_rendered(result: LeanWorkerMetaResult<LeanWorkerRendered>) -> MetaOutcome {
456 match result {
457 LeanWorkerMetaResult::Ok { value } => MetaOutcome {
458 status: "Ok".into(),
459 rendered: Some(value.value),
460 definitionally_equal: None,
461 failure: None,
462 raw_fallback_used: matches!(value.rendering, LeanWorkerRendering::Raw),
463 },
464 LeanWorkerMetaResult::Failed { failure } => meta_failure("Failed", &failure),
465 LeanWorkerMetaResult::TimeoutOrHeartbeat { failure } => meta_failure("TimeoutOrHeartbeat", &failure),
466 LeanWorkerMetaResult::Unsupported { failure } => meta_failure("Unsupported", &failure),
467 _ => MetaOutcome {
468 status: "Unsupported".into(),
469 rendered: None,
470 definitionally_equal: None,
471 failure: None,
472 raw_fallback_used: false,
473 },
474 }
475}
476
477pub fn project_meta_bool(result: LeanWorkerMetaResult<bool>) -> MetaOutcome {
478 match result {
479 LeanWorkerMetaResult::Ok { value } => MetaOutcome {
480 status: "Ok".into(),
481 rendered: None,
482 definitionally_equal: Some(value),
483 failure: None,
484 raw_fallback_used: false,
485 },
486 LeanWorkerMetaResult::Failed { failure } => meta_failure("Failed", &failure),
487 LeanWorkerMetaResult::TimeoutOrHeartbeat { failure } => meta_failure("TimeoutOrHeartbeat", &failure),
488 LeanWorkerMetaResult::Unsupported { failure } => meta_failure("Unsupported", &failure),
489 _ => MetaOutcome {
490 status: "Unsupported".into(),
491 rendered: None,
492 definitionally_equal: None,
493 failure: None,
494 raw_fallback_used: false,
495 },
496 }
497}
498
499fn meta_failure(status: &str, failure: &LeanWorkerElabFailure) -> MetaOutcome {
500 MetaOutcome {
501 status: status.to_owned(),
502 rendered: None,
503 definitionally_equal: None,
504 failure: Some(project_failure(failure)),
505 raw_fallback_used: false,
506 }
507}
508
509pub fn project_declaration_row(row: LeanWorkerDeclarationRow) -> DeclarationRow {
510 DeclarationRow {
511 name: row.name,
512 kind: row.kind,
513 type_signature: row.type_signature,
514 source: row.source.map(project_source_range),
515 }
516}
517
518pub(crate) fn project_rendered_info(info: LeanWorkerRenderedInfo) -> RenderedText {
519 RenderedText {
520 value: info.value,
521 truncated: info.truncated,
522 }
523}
524
525fn rendering_label(rendering: LeanWorkerRendering) -> String {
526 match rendering {
527 LeanWorkerRendering::Pretty => "pretty".to_owned(),
528 LeanWorkerRendering::Raw => "raw".to_owned(),
529 _ => "raw".to_owned(),
533 }
534}
535
536pub(crate) fn project_module_source_span(span: LeanWorkerModuleSourceSpan) -> ModuleSourceSpan {
537 ModuleSourceSpan {
538 start_line: span.start_line,
539 start_column: span.start_column,
540 end_line: span.end_line,
541 end_column: span.end_column,
542 }
543}
544
545pub(crate) fn project_proof_action_target(info: LeanWorkerDeclarationTargetInfo) -> ProofActionDeclarationTarget {
546 ProofActionDeclarationTarget {
547 short_name: info.short_name,
548 declaration_name: info.declaration_name,
549 namespace_name: info.namespace_name,
550 declaration_kind: info.declaration_kind,
551 declaration_span: project_module_source_span(info.declaration_span),
552 name_span: project_module_source_span(info.name_span),
553 body_span: project_module_source_span(info.body_span),
554 }
555}
556
557pub(crate) fn project_declaration_flags(flags: LeanWorkerDeclarationFlags) -> DeclarationFlags {
558 DeclarationFlags {
559 is_private: flags.is_private,
560 is_generated: flags.is_generated,
561 is_internal: flags.is_internal,
562 }
563}
564
565fn project_declaration_summary(row: LeanWorkerDeclarationSearchRow) -> DeclarationSummary {
566 DeclarationSummary {
567 name: row.name,
568 kind: row.kind,
569 module: row.module,
570 source: row.source.map(project_source_range),
571 match_reason: row.match_reason,
572 score: row.score,
573 rank: row.rank,
574 flags: project_declaration_flags(row.flags),
575 }
576}
577
578fn project_declaration_search_pruning(row: LeanWorkerDeclarationSearchPruning) -> DeclarationSearchPruning {
579 DeclarationSearchPruning {
580 stage: row.stage,
581 reason: row.reason,
582 count: row.count,
583 }
584}
585
586fn project_declaration_search_timings(timings: LeanWorkerDeclarationSearchTimings) -> DeclarationSearchTimings {
587 DeclarationSearchTimings {
588 scan_micros: timings.scan_micros,
589 rank_micros: timings.rank_micros,
590 source_micros: timings.source_micros,
591 }
592}
593
594fn project_declaration_search_facts(facts: LeanWorkerDeclarationSearchFacts) -> DeclarationSearchFacts {
595 DeclarationSearchFacts {
596 declarations_scanned: facts.declarations_scanned,
597 after_name_filter: facts.after_name_filter,
598 after_kind_filter: facts.after_kind_filter,
599 after_required_constants_filter: facts.after_required_constants_filter,
600 after_conclusion_filter: facts.after_conclusion_filter,
601 after_scope_filter: facts.after_scope_filter,
602 source_lookups: facts.source_lookups,
603 broad_pruning: facts
604 .broad_pruning
605 .into_iter()
606 .map(project_declaration_search_pruning)
607 .collect(),
608 truncated: facts.truncated,
609 timings: project_declaration_search_timings(facts.timings),
610 }
611}
612
613pub fn project_declaration_search(result: LeanWorkerDeclarationSearchResult) -> DeclarationSearchResult {
614 DeclarationSearchResult {
615 declarations: result
616 .declarations
617 .into_iter()
618 .map(project_declaration_summary)
619 .collect(),
620 truncated: result.truncated,
621 facts: project_declaration_search_facts(result.facts),
622 }
623}
624
625pub fn project_declaration_inspection(result: LeanWorkerDeclarationInspectionResult) -> DeclarationInspectionResult {
626 match result {
627 LeanWorkerDeclarationInspectionResult::Found { declaration } => DeclarationInspectionResult::Found {
628 declaration: Box::new(project_inspection(*declaration)),
629 },
630 LeanWorkerDeclarationInspectionResult::NotFound { name } => {
631 DeclarationInspectionResult::NotFound { name: Some(name) }
632 }
633 LeanWorkerDeclarationInspectionResult::Unsupported => DeclarationInspectionResult::Unsupported,
634 _ => DeclarationInspectionResult::Unsupported,
641 }
642}
643
644pub fn project_proof_attempt(result: LeanWorkerProofAttemptResult) -> ProofAttemptResult {
645 match result {
646 LeanWorkerProofAttemptResult::Ok { result, imports } => ProofAttemptResult::Ok {
647 result: project_proof_attempt_envelope(result),
648 imports,
649 },
650 LeanWorkerProofAttemptResult::MissingImports {
651 result,
652 imports,
653 missing,
654 } => ProofAttemptResult::MissingImports {
655 result: project_proof_attempt_envelope(result),
656 imports,
657 missing,
658 },
659 LeanWorkerProofAttemptResult::HeaderParseFailed { diagnostics } => ProofAttemptResult::HeaderParseFailed {
660 diagnostics: project_failure(&diagnostics),
661 },
662 LeanWorkerProofAttemptResult::Unsupported => ProofAttemptResult::Unsupported,
663 _ => ProofAttemptResult::Unsupported,
664 }
665}
666
667pub(crate) fn project_proof_attempt_envelope(envelope: LeanWorkerProofAttemptEnvelope) -> ProofAttemptEnvelope {
668 ProofAttemptEnvelope {
669 candidates: envelope.candidates.into_iter().map(project_proof_attempt_row).collect(),
670 candidate_limit: envelope.candidate_limit,
671 candidates_truncated: envelope.candidates_truncated,
672 }
673}
674
675pub(crate) fn project_proof_attempt_row(row: LeanWorkerProofAttemptRow) -> ProofAttemptCandidate {
676 ProofAttemptCandidate {
677 id: row.id,
678 status: proof_attempt_status(row.status).to_owned(),
679 snippet: project_rendered_info(row.candidate_text),
680 diagnostics: project_failure(&row.diagnostics),
681 downstream_diagnostics: project_failure(&row.downstream_diagnostics),
682 goals: row.goals.into_iter().map(project_rendered_info).collect(),
683 declaration: row.declaration.map(project_proof_action_target),
684 proof_position: row.proof_position.map(|position| ProofPositionSummary {
685 index: position.index,
686 tactic: project_rendered_info(position.tactic),
687 }),
688 output_truncated: row.output_truncated,
689 }
690}
691
692fn proof_attempt_status(status: LeanWorkerProofAttemptStatus) -> &'static str {
693 match status {
694 LeanWorkerProofAttemptStatus::Closed => "closed",
695 LeanWorkerProofAttemptStatus::Progressed => "progressed",
696 LeanWorkerProofAttemptStatus::Failed => "failed",
697 LeanWorkerProofAttemptStatus::Timeout => "timeout",
698 LeanWorkerProofAttemptStatus::BudgetExceeded => "budget_exceeded",
699 LeanWorkerProofAttemptStatus::Unsupported => "unsupported",
700 _ => "unsupported",
701 }
702}
703
704pub fn project_declaration_verification(
705 result: LeanWorkerDeclarationVerificationResult,
706) -> DeclarationVerificationResult {
707 match result {
708 LeanWorkerDeclarationVerificationResult::Ok {
709 verification_status,
710 facts,
711 imports,
712 } => {
713 let label = verification_status_label(verification_status, &facts);
714 let trustworthy = label != crate::diagnosis::NEEDS_BUILD_STATUS && label != "ambiguous";
718 DeclarationVerificationResult::Ok {
719 verification_status: label.to_owned(),
720 facts: Box::new(project_declaration_verification_facts(*facts, trustworthy)),
721 imports,
722 }
723 }
724 LeanWorkerDeclarationVerificationResult::MissingImports {
725 verification_status,
726 facts,
727 imports,
728 missing,
729 } => DeclarationVerificationResult::MissingImports {
730 verification_status: verification_status_label(verification_status, &facts).to_owned(),
731 facts: Box::new(project_declaration_verification_facts(*facts, false)),
734 imports,
735 missing,
736 },
737 LeanWorkerDeclarationVerificationResult::HeaderParseFailed { diagnostics } => {
738 DeclarationVerificationResult::HeaderParseFailed {
739 diagnostics: project_failure(&diagnostics),
740 }
741 }
742 LeanWorkerDeclarationVerificationResult::Unsupported => DeclarationVerificationResult::Unsupported,
743 _ => DeclarationVerificationResult::Unsupported,
744 }
745}
746
747fn project_declaration_verification_facts(
748 facts: LeanWorkerDeclarationVerificationFacts,
749 facts_trustworthy: bool,
750) -> DeclarationVerificationFacts {
751 DeclarationVerificationFacts {
752 target: facts.target.map(project_proof_action_target),
753 diagnostics: project_failure(&facts.diagnostics),
754 unresolved_goals: facts.unresolved_goals.into_iter().map(project_rendered_info).collect(),
755 contains_sorry: facts.contains_sorry,
756 contains_admit: facts.contains_admit,
757 contains_sorry_ax: facts.contains_sorry_ax,
758 axioms: facts.axioms,
759 axioms_truncated: facts.axioms_truncated,
760 axioms_available: facts.axioms_available,
761 output_truncated: facts.output_truncated,
762 candidates: facts.candidates.into_iter().map(project_proof_action_target).collect(),
763 facts_trustworthy,
764 }
765}
766
767fn verification_status_label(
768 status: LeanWorkerDeclarationVerificationStatus,
769 facts: &LeanWorkerDeclarationVerificationFacts,
770) -> &'static str {
771 match status {
772 LeanWorkerDeclarationVerificationStatus::Accepted => "verified",
773 LeanWorkerDeclarationVerificationStatus::Rejected if facts.contains_sorry => "has_sorry",
774 LeanWorkerDeclarationVerificationStatus::Rejected if !facts.unresolved_goals.is_empty() => {
775 "has_unresolved_goals"
776 }
777 LeanWorkerDeclarationVerificationStatus::Rejected if !facts.diagnostics.diagnostics.is_empty() => {
778 "has_diagnostics"
779 }
780 LeanWorkerDeclarationVerificationStatus::Rejected => "has_diagnostics",
781 LeanWorkerDeclarationVerificationStatus::NotFound => "not_found",
782 LeanWorkerDeclarationVerificationStatus::Ambiguous => "ambiguous",
785 LeanWorkerDeclarationVerificationStatus::NeedsBuild => crate::diagnosis::NEEDS_BUILD_STATUS,
788 LeanWorkerDeclarationVerificationStatus::Timeout => "timeout",
789 LeanWorkerDeclarationVerificationStatus::BudgetExceeded => "budget_exceeded",
790 LeanWorkerDeclarationVerificationStatus::Unsupported => "unsupported",
791 _ => "unsupported",
792 }
793}
794
795pub(crate) fn project_inspection(declaration: WorkerDeclarationInspection) -> DeclarationInspection {
796 DeclarationInspection {
797 name: declaration.name,
798 kind: declaration.kind,
799 module: declaration.module,
800 source: declaration.source.map(project_source_range),
801 statement: declaration.statement.map(project_rendered_info),
802 statement_rendering: declaration.statement_rendering.map(rendering_label),
803 docstring: declaration.docstring.map(project_rendered_info),
804 attributes: declaration.attributes,
805 proof_search: project_proof_search_facts(declaration.proof_search),
806 flags: project_declaration_flags(declaration.flags),
807 }
808}
809
810fn project_proof_search_facts(facts: LeanWorkerDeclarationProofSearchFacts) -> DeclarationProofSearchFacts {
811 DeclarationProofSearchFacts {
812 is_simp: facts.is_simp,
813 is_rw_candidate: facts.is_rw_candidate,
814 is_instance: facts.is_instance,
815 is_class: facts.is_class,
816 class_name: facts.class_name,
817 }
818}
819
820#[cfg(test)]
821#[allow(
822 clippy::unwrap_used,
823 clippy::panic,
824 reason = "unit tests fail directly on the wrong variant"
825)]
826mod tests {
827 use super::*;
828
829 fn clean_facts() -> LeanWorkerDeclarationVerificationFacts {
830 LeanWorkerDeclarationVerificationFacts {
831 target: None,
832 diagnostics: LeanWorkerElabFailure {
833 diagnostics: Vec::new(),
834 truncated: false,
835 },
836 unresolved_goals: Vec::new(),
837 contains_sorry: false,
838 contains_admit: false,
839 contains_sorry_ax: false,
840 axioms: Vec::new(),
841 axioms_truncated: false,
842 axioms_available: true,
843 output_truncated: false,
844 candidates: Vec::new(),
845 }
846 }
847
848 fn target_info(name: &str) -> LeanWorkerDeclarationTargetInfo {
849 let span = lean_rs_worker_parent::LeanWorkerModuleSourceSpan {
850 start_line: 0,
851 start_column: 0,
852 end_line: 0,
853 end_column: 0,
854 };
855 LeanWorkerDeclarationTargetInfo {
856 short_name: name.rsplit('.').next().unwrap_or(name).to_owned(),
857 declaration_name: name.to_owned(),
858 namespace_name: name.rsplit_once('.').map(|(ns, _)| ns.to_owned()).unwrap_or_default(),
859 declaration_kind: "theorem".to_owned(),
860 declaration_span: span.clone(),
861 name_span: span.clone(),
862 body_span: span,
863 }
864 }
865
866 #[test]
867 fn needs_build_verification_is_labeled_needs_build_with_untrustworthy_facts() {
868 let result = project_declaration_verification(LeanWorkerDeclarationVerificationResult::Ok {
869 verification_status: LeanWorkerDeclarationVerificationStatus::NeedsBuild,
870 facts: Box::new(clean_facts()),
871 imports: Vec::new(),
872 });
873 let DeclarationVerificationResult::Ok {
874 verification_status,
875 facts,
876 ..
877 } = result
878 else {
879 panic!("expected an Ok verdict");
880 };
881 assert_eq!(verification_status, "needs_build");
882 assert!(!facts.facts_trustworthy);
886 assert!(!facts.contains_sorry);
887 }
888
889 #[test]
890 fn ambiguous_verification_surfaces_candidates_with_untrustworthy_facts() {
891 let mut facts = clean_facts();
892 facts.candidates = vec![target_info("A.foo"), target_info("B.foo")];
893 let result = project_declaration_verification(LeanWorkerDeclarationVerificationResult::Ok {
894 verification_status: LeanWorkerDeclarationVerificationStatus::Ambiguous,
895 facts: Box::new(facts),
896 imports: Vec::new(),
897 });
898 let DeclarationVerificationResult::Ok {
899 verification_status,
900 facts,
901 ..
902 } = result
903 else {
904 panic!("expected an Ok verdict");
905 };
906 assert_eq!(verification_status, "ambiguous");
909 assert_eq!(facts.candidates.len(), 2);
910 assert!(!facts.facts_trustworthy);
911 }
912
913 #[test]
914 fn accepted_verification_keeps_facts_trustworthy() {
915 let result = project_declaration_verification(LeanWorkerDeclarationVerificationResult::Ok {
916 verification_status: LeanWorkerDeclarationVerificationStatus::Accepted,
917 facts: Box::new(clean_facts()),
918 imports: Vec::new(),
919 });
920 let DeclarationVerificationResult::Ok {
921 verification_status,
922 facts,
923 ..
924 } = result
925 else {
926 panic!("expected an Ok verdict");
927 };
928 assert_eq!(verification_status, "verified");
929 assert!(facts.facts_trustworthy);
930 }
931}