fbxcel/writer/v7400/binary/
macros.rs

1//! Macros.
2
3/// Drives the given writer to write the given tree as FBX binary, and returns
4/// the `fbxcel::writer::v7400::binary::Result<()>`.
5///
6/// Enabled by `writer` feature.
7///
8/// # Examples
9///
10/// ```
11/// # use fbxcel::write_v7400_binary;
12/// use fbxcel::{low::FbxVersion, writer::v7400::binary::Writer};
13/// let mut writer = Writer::new(std::io::Cursor::new(Vec::new()), FbxVersion::V7_4)?;
14///
15/// write_v7400_binary!(
16///     writer=writer,
17///     tree={
18///         Node0: {
19///             Node0_0: {}
20///             Node0_1: {}
21///         }
22///         Node1: {
23///             // You can use trailing comma.
24///             Node1_0: {},
25///             Node1_1: {},
26///         }
27///         // Use parens to specify attributes by single array.
28///         // Note that the expression inside parens should implement
29///         // `IntoIterator<Item = AttributeValue>`.
30///         Node2: (vec!["hello".into(), "world".into(), 42i32.into()]) {}
31///         // Use brackets to specify attributes one by one.
32///         Node3: ["hello", "world", 1.234f32, &b"BINARY"[..]] {}
33///     },
34/// )?;
35/// let _buf = writer.finalize_and_flush(&Default::default())?;
36/// # Ok::<_, Box<dyn std::error::Error>>(())
37/// ```
38#[cfg_attr(docsrs, doc(cfg(feature = "writer")))]
39#[macro_export]
40macro_rules! write_v7400_binary {
41    (
42        writer=$writer:expr,
43        tree={$($tree:tt)*},
44    ) => {{
45        let mut f = || -> $crate::writer::v7400::binary::Result<()> {
46            let _writer = &mut $writer;
47            write_v7400_binary! { @__node, _writer, $($tree)* };
48            Ok(())
49        };
50        f()
51    }};
52
53
54    (@__node, $writer:ident,) => {};
55    (@__node, $writer:ident, , $($tree:tt)*) => {
56        write_v7400_binary! { @__node, $writer, $($tree)* }
57    };
58
59    (@__node, $writer:ident,
60        $name:ident: {
61            $($subtree:tt)*
62        }
63        $($rest:tt)*
64    ) => {{
65        $writer.new_node(stringify!($name))?;
66        write_v7400_binary! { @__node, $writer, $($subtree)* }
67        $writer.close_node()?;
68        write_v7400_binary! { @__node, $writer, $($rest)* }
69    }};
70    (@__node, $writer:ident,
71        $name:ident: [$($attr:expr),* $(,)?] {
72            $($subtree:tt)*
73        }
74        $($rest:tt)*
75    ) => {{
76        let mut _attrs = $writer.new_node(stringify!($name))?;
77        $({
78            let attr = $attr;
79            write_v7400_binary!(@__attr, _attrs, attr.into())?;
80        })*
81        write_v7400_binary! { @__node, $writer, $($subtree)* }
82        $writer.close_node()?;
83        write_v7400_binary! { @__node, $writer, $($rest)* }
84    }};
85    (@__node, $writer:ident,
86        $name:ident: ($attrs:expr) {
87            $($subtree:tt)*
88        }
89        $($rest:tt)*
90    ) => {{
91        let mut _attrs = $writer.new_node(stringify!($name))?;
92        $attrs.into_iter().try_for_each(|attr: $crate::low::v7400::AttributeValue| {
93            write_v7400_binary!(@__attr, _attrs, attr.into())
94        })?;
95        write_v7400_binary! { @__node, $writer, $($subtree)* }
96        $writer.close_node()?;
97        write_v7400_binary! { @__node, $writer, $($rest)* }
98    }};
99
100    (@__attr, $attrs:ident, $attr:expr) => {{
101        use $crate::low::v7400::AttributeValue::*;
102        match $attr {
103            Bool(v) => $attrs.append_bool(v),
104            I16(v) => $attrs.append_i16(v),
105            I32(v) => $attrs.append_i32(v),
106            I64(v) => $attrs.append_i64(v),
107            F32(v) => $attrs.append_f32(v),
108            F64(v) => $attrs.append_f64(v),
109            ArrBool(v) => $attrs.append_arr_bool_from_iter(None, v),
110            ArrI32(v) => $attrs.append_arr_i32_from_iter(None, v),
111            ArrI64(v) => $attrs.append_arr_i64_from_iter(None, v),
112            ArrF32(v) => $attrs.append_arr_f32_from_iter(None, v),
113            ArrF64(v) => $attrs.append_arr_f64_from_iter(None, v),
114            Binary(v) => $attrs.append_binary_direct(&v),
115            String(v) => $attrs.append_string_direct(&v),
116        }
117    }};
118}
119
120#[cfg(test)]
121mod tests {
122    use std::io::Cursor;
123
124    use crate::{
125        low::FbxVersion,
126        writer::v7400::binary::{Result, Writer},
127    };
128
129    #[test]
130    fn empty_writer() -> Result<()> {
131        let mut writer = Writer::new(Cursor::new(Vec::new()), FbxVersion::V7_4)?;
132        write_v7400_binary!(writer = writer, tree = {},)?;
133        let _buf = writer.finalize_and_flush(&Default::default())?;
134
135        Ok(())
136    }
137
138    #[test]
139    fn empty_node() -> Result<()> {
140        let mut writer = Writer::new(Cursor::new(Vec::new()), FbxVersion::V7_4)?;
141        write_v7400_binary!(
142            writer=writer,
143            tree={
144                Hello: {}
145                World: {},
146            },
147        )?;
148        let _buf = writer.finalize_and_flush(&Default::default())?;
149
150        Ok(())
151    }
152
153    #[test]
154    fn nested_node() -> Result<()> {
155        let mut writer = Writer::new(Cursor::new(Vec::new()), FbxVersion::V7_4)?;
156        write_v7400_binary!(
157            writer=writer,
158            tree={
159                Hello: {
160                    Hello1: {},
161                    Hello2: {}
162                }
163                World: {
164                    World1: {
165                        World1_1: {}
166                        World1_2: {}
167                    }
168                    World2: {},
169                },
170            },
171        )?;
172        let _buf = writer.finalize_and_flush(&Default::default())?;
173
174        Ok(())
175    }
176
177    #[test]
178    fn nested_node_with_attrs() -> Result<()> {
179        let mut writer = Writer::new(Cursor::new(Vec::new()), FbxVersion::V7_4)?;
180        write_v7400_binary!(
181            writer=writer,
182            tree={
183                Hello: {
184                    Hello1: (vec!["string".into()]) {},
185                    Hello2: [1.234f32, 42i64] {}
186                }
187                World: {
188                    World1: {
189                        World1_1: (vec!["Hello".into(), 42i32.into()]) {}
190                        World1_2: [] {}
191                    }
192                    World2: {},
193                },
194            },
195        )?;
196        let _buf = writer.finalize_and_flush(&Default::default())?;
197
198        Ok(())
199    }
200}