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