Skip to main content

mlt_ffi/
lib.rs

1#[expect(
2    clippy::unnecessary_box_returns,
3    reason = "Diplomat requires `Box<T>` returns for opaque constructors"
4)]
5#[expect(
6    clippy::use_self,
7    reason = "Diplomat expands macros using the concrete type name rather than `Self`."
8)]
9#[diplomat::bridge]
10mod ffi {
11    use mlt_core::encoder::EncoderConfig;
12    use mlt_core::mvt::{mvt_to_tile_layers, tile_layers_to_mvt};
13    use mlt_core::{Decoder, Layer, Parser};
14
15    /// Error type returned by FFI conversion functions.
16    #[diplomat::attr(auto, error)]
17    pub enum ConvertError {
18        /// Input bytes could not be parsed or decoded.
19        InvalidInput,
20        /// Encoding failed.
21        EncodingFailed,
22    }
23
24    /// Owned byte buffer returned from conversion functions.
25    ///
26    /// The caller borrows the contents via [`as_bytes`](MltBuffer::as_bytes)
27    /// and the buffer is freed when the handle is dropped.
28    #[diplomat::opaque]
29    pub struct MltBuffer(Vec<u8>);
30
31    impl MltBuffer {
32        /// Borrow the contents as a byte slice.
33        #[diplomat::attr(auto, getter = "bytes")]
34        #[expect(
35            clippy::needless_lifetimes,
36            reason = "diplomat requires explicit lifetimes"
37        )]
38        pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
39            &self.0
40        }
41
42        /// Number of bytes in the buffer.
43        #[diplomat::attr(auto, getter = "len")]
44        pub fn len(&self) -> usize {
45            self.0.len()
46        }
47    }
48
49    /// Encoder options controlling which optimisations are attempted for
50    /// MVT -> MLT conversion.
51    ///
52    /// Construct with [`new`](MltEncoderOptions::new) (all optimisations
53    /// enabled except tessellation) and toggle individual flags with the
54    /// setter methods.
55    #[diplomat::opaque_mut]
56    pub struct MltEncoderOptions(EncoderConfig);
57
58    impl MltEncoderOptions {
59        /// Create encoder options with the default configuration (all
60        /// optimisations enabled except tessellation).
61        #[diplomat::attr(auto, constructor)]
62        pub fn new() -> Box<MltEncoderOptions> {
63            Box::new(MltEncoderOptions(EncoderConfig::default()))
64        }
65
66        /// Generate tessellation data for polygons and multi-polygons.
67        pub fn set_tessellate(&mut self, enabled: bool) {
68            self.0 = self.0.with_tessellation(enabled);
69        }
70
71        /// Try sorting features by the Z-order (Morton) curve index.
72        pub fn set_attempt_spatial_morton_sort(&mut self, enabled: bool) {
73            self.0 = self.0.with_spatial_morton_sort(enabled);
74        }
75
76        /// Try sorting features by the Hilbert curve index.
77        pub fn set_attempt_spatial_hilbert_sort(&mut self, enabled: bool) {
78            self.0 = self.0.with_spatial_hilbert_sort(enabled);
79        }
80
81        /// Try sorting features by their feature ID in ascending order.
82        pub fn set_attempt_id_sort(&mut self, enabled: bool) {
83            self.0 = self.0.with_id_sort(enabled);
84        }
85
86        /// Allow FSST string compression.
87        pub fn set_allow_fsst(&mut self, enabled: bool) {
88            self.0 = self.0.with_fsst(enabled);
89        }
90
91        /// Allow `FastPFOR` integer compression.
92        pub fn set_allow_fastpfor(&mut self, enabled: bool) {
93            self.0 = self.0.with_fastpfor(enabled);
94        }
95
96        /// Allow string grouping into shared dictionaries.
97        pub fn set_allow_shared_dict(&mut self, enabled: bool) {
98            self.0 = self.0.with_shared_dict(enabled);
99        }
100    }
101
102    /// Stateless FFI entry-points for MLT <-> MVT conversion.
103    #[diplomat::opaque]
104    pub struct MltConverter;
105
106    impl MltConverter {
107        /// Decode MLT bytes into MVT bytes.
108        pub fn mlt_to_mvt(mlt: &[u8]) -> Result<Box<MltBuffer>, ConvertError> {
109            let layers = Parser::default()
110                .parse_layers(mlt)
111                .map_err(|_| ConvertError::InvalidInput)?;
112            let mut dec = Decoder::default();
113            let mut tiles = Vec::new();
114            for layer in layers {
115                if let Layer::Tag01(l) = layer {
116                    let tile = l
117                        .into_tile(&mut dec)
118                        .map_err(|_| ConvertError::InvalidInput)?;
119                    tiles.push(tile);
120                }
121            }
122            let out = tile_layers_to_mvt(tiles).map_err(|_| ConvertError::InvalidInput)?;
123            Ok(Box::new(MltBuffer(out)))
124        }
125
126        /// Encode MVT bytes into MLT bytes using the given encoder options.
127        pub fn mvt_to_mlt(
128            mvt: &[u8],
129            options: &MltEncoderOptions,
130        ) -> Result<Box<MltBuffer>, ConvertError> {
131            let mut out = Vec::new();
132            let layers = mvt_to_tile_layers(mvt).map_err(|_| ConvertError::EncodingFailed)?;
133            for tile in layers {
134                let encoded_tile = tile
135                    .encode(options.0)
136                    .map_err(|_| ConvertError::EncodingFailed)?;
137                out.extend_from_slice(&encoded_tile);
138            }
139            Ok(Box::new(MltBuffer(out)))
140        }
141    }
142}