Skip to main content

classifier_implementation/
lib.rs

1//! Governed classifier use-case boundary over the ordinary classifier engine.
2#![deny(missing_docs)]
3#![forbid(unsafe_code)]
4
5use boxology_classifier::{Class, ClassificationReport, classify};
6use boxology_schema::SchemaDocument;
7
8mod contract;
9pub use contract::*;
10
11/// Pure implementation of the generated classifier capability.
12pub struct ClassifierService;
13
14#[boxology::implementation]
15impl ClassifierService {
16    /// Classifies strict canonical schema bytes without consulting ambient state.
17    pub async fn classify(
18        &self,
19        _context: boxology::CallContext,
20        request: ClassifyRequest,
21    ) -> Result<ClassifyOutcome, ClassifierError> {
22        let base = match request.base.as_deref() {
23            Some(bytes) => match SchemaDocument::parse(bytes) {
24                Ok(document) => Some(document),
25                Err(diagnostics) => {
26                    return Ok(failure(ClassifyFailureStage::Base, diagnostics));
27                }
28            },
29            None => None,
30        };
31        let submitted = match SchemaDocument::parse(&request.submitted) {
32            Ok(document) => document,
33            Err(diagnostics) => {
34                return Ok(failure(ClassifyFailureStage::Submitted, diagnostics));
35            }
36        };
37        let report = match classify(base.as_ref(), Some(&submitted)) {
38            Ok(report) => report,
39            Err(diagnostics) => {
40                return Ok(failure(ClassifyFailureStage::Pairing, diagnostics));
41            }
42        };
43        Ok(ClassifyOutcome {
44            report: Some(boundary_report(&report)),
45            failure: None,
46        })
47    }
48}
49
50fn failure(
51    stage: ClassifyFailureStage,
52    diagnostics: boxology_schema::Diagnostics,
53) -> ClassifyOutcome {
54    ClassifyOutcome {
55        report: None,
56        failure: Some(ClassifyFailure {
57            stage,
58            diagnostics: diagnostics.to_string(),
59        }),
60    }
61}
62
63fn boundary_report(report: &ClassificationReport) -> ClassifyReport {
64    ClassifyReport {
65        verdict: boundary_class(report.verdict()),
66        findings: report
67            .findings()
68            .iter()
69            .map(|finding| ClassifyFinding {
70                code: finding.code().to_owned(),
71                path: finding.path().to_owned(),
72                kind: finding.kind().to_owned(),
73                class: boundary_class(finding.class()),
74                base_excerpt: finding.base_excerpt().map(str::to_owned),
75                submitted_excerpt: finding.submitted_excerpt().map(str::to_owned),
76                condition: finding.condition().map(str::to_owned),
77            })
78            .collect(),
79        rendered_text: boxology_classifier::render_text(report),
80    }
81}
82
83fn boundary_class(class: Class) -> CompatibilityClass {
84    match class {
85        Class::Unchanged => CompatibilityClass::Unchanged,
86        Class::Documentation => CompatibilityClass::Documentation,
87        Class::Deprecation => CompatibilityClass::Deprecation,
88        Class::Additive => CompatibilityClass::Additive,
89        Class::CompatibleWithConditions => CompatibilityClass::CompatibleWithConditions,
90        Class::Incompatible => CompatibilityClass::Incompatible,
91    }
92}
93
94/// Generated implementation adapter for composition assembly.
95#[doc(hidden)]
96pub mod generated {
97    include!("generated_adapter.rs");
98}