Skip to main content

boxferry_engine/
lib.rs

1//! Conversion planning, policy, capability, and diagnostics for `BoxFerry`.
2
3mod adapter;
4mod diagnostic;
5mod outcome;
6mod rule;
7mod target;
8
9pub use adapter::{ConversionError, ExportAdapter, ImportAdapter, ImportResult, InMemoryAdapter, convert};
10pub use diagnostic::{
11    Diagnostic, DiagnosticCode, DiagnosticField, DiagnosticValue, InvalidDiagnosticCode, NativeFinding,
12    NativeFindingLabel, NativeFindingLabelKind, Severity,
13};
14pub use outcome::{ConversionKind, ConversionOutcome, ConversionPlan, ConversionResult, LossPolicy, PlanError};
15pub use rule::{DiagnosticRule, RULES, RuleId, find_rule};
16pub use target::{ParsePlatformVersionError, PlatformVersion, TargetProfile, TargetProfileError, VersionRange};
17
18#[cfg(test)]
19mod integration_tests {
20    use boxferry_model::{Application, Identifier};
21
22    use crate::{
23        ConversionKind, ConversionOutcome, Diagnostic, DiagnosticCode, ImportAdapter, ImportResult, InMemoryAdapter,
24        LossPolicy, PlatformVersion, Severity, TargetProfile, convert,
25    };
26
27    struct LossyImporter {
28        application: Application,
29    }
30
31    impl ImportAdapter for LossyImporter {
32        type Source = ();
33
34        fn import(&self, _source: &Self::Source) -> ImportResult {
35            let code = DiagnosticCode::new("BFE0010").ok();
36            let (outcomes, diagnostics) = code.map_or_else(
37                || (Vec::new(), Vec::new()),
38                |code| {
39                    let outcome =
40                        ConversionOutcome::loss("services.web.extension", ConversionKind::Unsupported, code.clone())
41                            .ok();
42                    let outcomes = outcome.into_iter().collect();
43                    let diagnostics = vec![Diagnostic::new(code, Severity::Warning, "extension omitted")];
44                    (outcomes, diagnostics)
45                },
46            );
47            ImportResult::new(Some(self.application.clone()), outcomes, diagnostics)
48        }
49    }
50
51    #[test]
52    fn import_losses_participate_in_output_authorization() -> Result<(), String> {
53        let application = Application::new(Identifier::new("example").map_err(|error| error.to_string())?);
54        let importer = LossyImporter { application };
55        let exporter = InMemoryAdapter::exact("candidate".to_owned());
56        let target =
57            TargetProfile::new("target", PlatformVersion::new(1, 0, 0), None).map_err(|error| error.to_string())?;
58
59        let strict =
60            convert(&importer, &(), &exporter, &target, LossPolicy::ExactOnly).map_err(|error| error.to_string())?;
61        assert!(strict.is_blocked());
62
63        let partial =
64            convert(&importer, &(), &exporter, &target, LossPolicy::AllowPartial).map_err(|error| error.to_string())?;
65        assert_eq!(partial.output().map(String::as_str), Some("candidate"));
66        assert!(partial.outcomes().iter().any(|outcome| {
67            outcome.kind() == ConversionKind::Unsupported && outcome.subject() == "services.web.extension"
68        }));
69        Ok(())
70    }
71}