From 8c8f1c77e93d8982be58022c72bbbbf2c3cd0661 Mon Sep 17 00:00:00 2001 From: real Date: Fri, 28 Jun 2019 17:02:03 +0300 Subject: [PATCH 01/50] Initial outline for offst-capnp-conv. --- Cargo.toml | 17 +++++ LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ src/lib.rs | 129 +++++++++++++++++++++++++++++++ 4 files changed, 372 insertions(+) create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..1d954604d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "offst-capnp-conv" +version = "0.1.0" +authors = ["real "] +license = "MIT OR Apache-2.0" +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] + +quote = "0.6.12" +syn = "0.15.38" +proc-macro2 = "0.4" + + diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..c753170bc --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright (c) 2019 real + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..683ce3d46 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019 real + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..782c9b2b3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,129 @@ +#![crate_type = "lib"] +#![feature(nll)] +#![feature(generators)] +#![feature(never_type)] +#![deny(trivial_numeric_casts, warnings)] +#![allow(intra_doc_link_resolution_failure)] +#![allow( + clippy::too_many_arguments, + clippy::implicit_hasher, + clippy::module_inception, + clippy::new_without_default +)] + +extern crate proc_macro; + +// use quote::{quote, quote_spanned}; +// use syn::spanned::Spanned; +// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use syn::{parse_macro_input, DeriveInput, Ident}; + +/// Generate code for conversion between Rust and capnp structs. +#[proc_macro_attribute] +pub fn capnp_conv( + args: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + // See: https://github.com/dtolnay/syn/issues/86 + // for information about arguments. + + // Name of capnp struct: + let _capnp_struct_name = parse_macro_input!(args as Ident); + let input = parse_macro_input!(input as DeriveInput); + + // Name of local struct: + let _rust_struct_name = &input.ident; + + unimplemented!(); + + /* + let conversion = match input.data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields) => { + // Example: + // struct Point { + // x: u32, + // y: u32, + // } + let recurse1 = fields.named.iter().map(|f| { + let fname = &f.ident; + quote_spanned! { f.span() => + #fname: input.#fname + } + }); + // TODO: Is there a more elegant way to do this except cloning? + let recurse2 = recurse1.clone(); + quote! { + impl From<#local_name> for #remote_name { + fn from(input: #local_name) -> Self { + #remote_name { + #(#recurse1, )* + } + } + } + impl From<#remote_name> for #local_name { + fn from(input: #remote_name) -> Self { + #local_name { + #(#recurse2, )* + } + } + } + } + } + Fields::Unnamed(ref fields) => { + // Example: + // struct Pair(i32, f32); + + let recurse1 = fields.unnamed.iter().enumerate().map(|(i, f)| { + let index = Index::from(i); + // TODO: Should we use Index::from(i) here? + // What happens if we don't? + quote_spanned! { f.span() => + input.#index + } + }); + // TODO: Is there a more elegant way to do this except cloning? + let recurse2 = recurse1.clone(); + quote! { + impl From<#local_name> for #remote_name { + fn from(input: #local_name) -> Self { + #remote_name(#(#recurse1,)*) + } + } + impl From<#remote_name> for #local_name { + fn from(input: #remote_name) -> Self { + #local_name(#(#recurse2,)*) + } + } + } + } + Fields::Unit => { + // Example: + // struct MyStruct; + quote! { + impl From<#local_name> for #remote_name { + fn from(input: #local_name) -> Self { + #remote_name + } + } + impl From<#remote_name> for #local_name { + fn from(input: #remote_name) -> Self { + #local_name + } + } + } + } + }, + Data::Enum(_) | Data::Union(_) => unimplemented!(), + }; + + let expanded = quote! { + // Original structure + #input + // Generated mutual From conversion code: + #conversion + }; + + proc_macro::TokenStream::from(expanded) + */ +} From dfc3e3ff1f1073f8e76b51ad3ca4c61e586cb319 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 13:53:04 +0300 Subject: [PATCH 02/50] Initial work on capnp_conv_derive. Created basic outline. --- Cargo.toml | 14 ++- capnp_conv_derive/Cargo.toml | 18 +++ capnp_conv_derive/LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++ capnp_conv_derive/LICENSE-MIT | 25 ++++ capnp_conv_derive/src/lib.rs | 117 ++++++++++++++++++ src/lib.rs | 131 ++++---------------- 6 files changed, 392 insertions(+), 114 deletions(-) create mode 100644 capnp_conv_derive/Cargo.toml create mode 100644 capnp_conv_derive/LICENSE-APACHE create mode 100644 capnp_conv_derive/LICENSE-MIT create mode 100644 capnp_conv_derive/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1d954604d..7f81eceab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,13 +5,15 @@ authors = ["real "] license = "MIT OR Apache-2.0" edition = "2018" -[lib] -proc-macro = true - [dependencies] -quote = "0.6.12" -syn = "0.15.38" -proc-macro2 = "0.4" +log = "0.4" +pretty_env_logger = "0.2" + +futures-preview = "0.3.0-alpha.16" + +byteorder = "1.1" +capnp = "0.10.0" +derive_more = "0.15.0" diff --git a/capnp_conv_derive/Cargo.toml b/capnp_conv_derive/Cargo.toml new file mode 100644 index 000000000..56463a5c5 --- /dev/null +++ b/capnp_conv_derive/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "offst-capnp-conv-derive" +version = "0.1.0" +authors = ["real "] +license = "MIT OR Apache-2.0" +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] + +quote = "0.6.12" +syn = "0.15.38" +proc-macro2 = "0.4" + + + diff --git a/capnp_conv_derive/LICENSE-APACHE b/capnp_conv_derive/LICENSE-APACHE new file mode 100644 index 000000000..c753170bc --- /dev/null +++ b/capnp_conv_derive/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright (c) 2019 real + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/capnp_conv_derive/LICENSE-MIT b/capnp_conv_derive/LICENSE-MIT new file mode 100644 index 000000000..683ce3d46 --- /dev/null +++ b/capnp_conv_derive/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019 real + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs new file mode 100644 index 000000000..867e24880 --- /dev/null +++ b/capnp_conv_derive/src/lib.rs @@ -0,0 +1,117 @@ +#![crate_type = "lib"] +#![feature(nll)] +#![feature(generators)] +#![feature(never_type)] +#![deny(trivial_numeric_casts, warnings)] +#![allow(intra_doc_link_resolution_failure)] +#![allow( + clippy::too_many_arguments, + clippy::implicit_hasher, + clippy::module_inception, + clippy::new_without_default +)] + +extern crate proc_macro; + +// use quote::{quote, quote_spanned}; +use proc_macro2::TokenStream; +use quote::quote; +// use syn::spanned::Spanned; +// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident}; + +fn gen_into_capnp_named_struct( + _fields_named: &FieldsNamed, + _rust_struct: &Ident, + _capnp_struct: &Ident, +) -> TokenStream { + unimplemented!(); +} + +fn gen_from_capnp_named_struct( + _fields_named: &FieldsNamed, + _rust_struct: &Ident, + _capnp_struct: &Ident, +) -> TokenStream { + unimplemented!(); +} + +fn gen_into_capnp_enum( + _data_enum: &DataEnum, + _rust_struct: &Ident, + _capnp_struct: &Ident, +) -> TokenStream { + unimplemented!(); +} + +fn gen_from_capnp_enum( + _data_enum: &DataEnum, + _rust_struct: &Ident, + _capnp_struct: &Ident, +) -> TokenStream { + unimplemented!(); +} + +/// Generate code for conversion between Rust and capnp structs. +#[proc_macro_attribute] +pub fn capnp_conv( + args: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + // See: https://github.com/dtolnay/syn/issues/86 + // for information about arguments. + + // Name of capnp struct: + let capnp_struct = parse_macro_input!(args as Ident); + let input = parse_macro_input!(input as DeriveInput); + + // Name of local struct: + let rust_struct = &input.ident; + + let conversion = match input.data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields_named) => { + // Example: + // struct Point { + // x: u32, + // y: u32, + // } + let into_capnp = + gen_into_capnp_named_struct(fields_named, rust_struct, &capnp_struct); + let from_capnp = + gen_from_capnp_named_struct(fields_named, rust_struct, &capnp_struct); + + quote! { + #into_capnp + #from_capnp + } + } + Fields::Unnamed(_) | Fields::Unit => unimplemented!(), + }, + Data::Enum(ref data_enum) => { + // Example: + // enum MyEnum { + // Type1(u32), + // Type2, + // Type3(MyStruct), + // } + let into_capnp = gen_into_capnp_enum(data_enum, rust_struct, &capnp_struct); + let from_capnp = gen_from_capnp_enum(data_enum, rust_struct, &capnp_struct); + + quote! { + #into_capnp + #from_capnp + } + } + Data::Union(_) => unimplemented!(), + }; + + let expanded = quote! { + // Original structure + #input + // Generated mutual From conversion code: + #conversion + }; + + proc_macro::TokenStream::from(expanded) +} diff --git a/src/lib.rs b/src/lib.rs index 782c9b2b3..3ae48dd96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![crate_type = "lib"] +#![feature(async_await, await_macro, arbitrary_self_types)] #![feature(nll)] #![feature(generators)] #![feature(never_type)] @@ -11,119 +12,33 @@ clippy::new_without_default )] -extern crate proc_macro; +use std::io; -// use quote::{quote, quote_spanned}; -// use syn::spanned::Spanned; -// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{parse_macro_input, DeriveInput, Ident}; +use capnp; -/// Generate code for conversion between Rust and capnp structs. -#[proc_macro_attribute] -pub fn capnp_conv( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - // See: https://github.com/dtolnay/syn/issues/86 - // for information about arguments. +use derive_more::From; - // Name of capnp struct: - let _capnp_struct_name = parse_macro_input!(args as Ident); - let input = parse_macro_input!(input as DeriveInput); - - // Name of local struct: - let _rust_struct_name = &input.ident; - - unimplemented!(); +#[derive(Debug, From)] +pub enum SerCapnpError { + CapnpError(capnp::Error), + NotInSchema(capnp::NotInSchema), + IoError(io::Error), +} - /* - let conversion = match input.data { - Data::Struct(ref data) => match data.fields { - Fields::Named(ref fields) => { - // Example: - // struct Point { - // x: u32, - // y: u32, - // } - let recurse1 = fields.named.iter().map(|f| { - let fname = &f.ident; - quote_spanned! { f.span() => - #fname: input.#fname - } - }); - // TODO: Is there a more elegant way to do this except cloning? - let recurse2 = recurse1.clone(); - quote! { - impl From<#local_name> for #remote_name { - fn from(input: #local_name) -> Self { - #remote_name { - #(#recurse1, )* - } - } - } - impl From<#remote_name> for #local_name { - fn from(input: #remote_name) -> Self { - #local_name { - #(#recurse2, )* - } - } - } - } - } - Fields::Unnamed(ref fields) => { - // Example: - // struct Pair(i32, f32); +/// Convert Rust struct to Capnp. +pub trait IntoCapnp { + /// The corresponding Capnp writer type. + type WriterType; - let recurse1 = fields.unnamed.iter().enumerate().map(|(i, f)| { - let index = Index::from(i); - // TODO: Should we use Index::from(i) here? - // What happens if we don't? - quote_spanned! { f.span() => - input.#index - } - }); - // TODO: Is there a more elegant way to do this except cloning? - let recurse2 = recurse1.clone(); - quote! { - impl From<#local_name> for #remote_name { - fn from(input: #local_name) -> Self { - #remote_name(#(#recurse1,)*) - } - } - impl From<#remote_name> for #local_name { - fn from(input: #remote_name) -> Self { - #local_name(#(#recurse2,)*) - } - } - } - } - Fields::Unit => { - // Example: - // struct MyStruct; - quote! { - impl From<#local_name> for #remote_name { - fn from(input: #local_name) -> Self { - #remote_name - } - } - impl From<#remote_name> for #local_name { - fn from(input: #remote_name) -> Self { - #local_name - } - } - } - } - }, - Data::Enum(_) | Data::Union(_) => unimplemented!(), - }; + /// Converts a Rust struct to corresponding Capnp struct. This should not fail. + fn into_capnp(self, writer_type: &mut Self::WriterType); +} - let expanded = quote! { - // Original structure - #input - // Generated mutual From conversion code: - #conversion - }; +/// Convert Capnp struct to Rust. +pub trait FromCapnp: Sized { + /// The corresponding Capnp reader type. + type ReaderType; - proc_macro::TokenStream::from(expanded) - */ + /// Converts a Capnp struct to corresponding Rust struct. + fn from_capnp(object: &Self::ReaderType) -> Result; } From 1fd0e8d6cc828a5ed55a96edb3e6aa8f8676caf0 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 14:33:56 +0300 Subject: [PATCH 03/50] Some work on capnp named struct conversion. Not done yet. --- capnp_conv_derive/src/lib.rs | 56 +++++++++++++++++++++++++++++------- src/lib.rs | 10 +++---- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 867e24880..4e2a48c57 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -13,27 +13,61 @@ extern crate proc_macro; -// use quote::{quote, quote_spanned}; use proc_macro2::TokenStream; -use quote::quote; -// use syn::spanned::Spanned; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident}; fn gen_into_capnp_named_struct( - _fields_named: &FieldsNamed, - _rust_struct: &Ident, - _capnp_struct: &Ident, + fields_named: &FieldsNamed, + rust_struct: &Ident, + capnp_struct: &Ident, ) -> TokenStream { - unimplemented!(); + // let capnp_write = unimplemented!(); + + let recurse = fields_named.named.iter().map(|f| { + let name = &f.ident; + quote_spanned! {f.span() => + self.#name.write_capnp(&mut writer.reborrow().init_#name()); + } + }); + + quote! { + impl CapnpWriter for #rust_struct { + type WriterType = #capnp_struct::Writer; + + fn write_capnp(self, writer: &mut Self::WriterType) { + #(#recurse)* + } + } + } } fn gen_from_capnp_named_struct( - _fields_named: &FieldsNamed, - _rust_struct: &Ident, - _capnp_struct: &Ident, + fields_named: &FieldsNamed, + rust_struct: &Ident, + capnp_struct: &Ident, ) -> TokenStream { - unimplemented!(); + let recurse = fields_named.named.iter().map(|f| { + let name = &f.ident; + quote_spanned! {f.span() => + // route: deser_friends_route(&route_capacity_rate_reader.get_route()?)?, + #name: TODO_TYPE_NAME::read_capnp(&reader.get_#name()?)? + } + }); + + quote! { + impl CapnpReader for #rust_struct { + type ReaderType = #capnp_struct::Reader; + + fn read_capnp(reader: &mut Self::ReaderType) { + Ok(#rust_struct { + #(#recurse,)* + }) + } + } + } } fn gen_into_capnp_enum( diff --git a/src/lib.rs b/src/lib.rs index 3ae48dd96..7b1e64ea1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,26 +19,26 @@ use capnp; use derive_more::From; #[derive(Debug, From)] -pub enum SerCapnpError { +pub enum CapnpConvError { CapnpError(capnp::Error), NotInSchema(capnp::NotInSchema), IoError(io::Error), } /// Convert Rust struct to Capnp. -pub trait IntoCapnp { +pub trait WriteCapnp { /// The corresponding Capnp writer type. type WriterType; /// Converts a Rust struct to corresponding Capnp struct. This should not fail. - fn into_capnp(self, writer_type: &mut Self::WriterType); + fn write_capnp(self, writer: &mut Self::WriterType); } /// Convert Capnp struct to Rust. -pub trait FromCapnp: Sized { +pub trait ReadCapnp: Sized { /// The corresponding Capnp reader type. type ReaderType; /// Converts a Capnp struct to corresponding Rust struct. - fn from_capnp(object: &Self::ReaderType) -> Result; + fn read_capnp(reader: &Self::ReaderType) -> Result; } From 410062f66978b59629a1de2f55a6d1ef4b397c55 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 14:35:57 +0300 Subject: [PATCH 04/50] Renamed functions: into -> write, from -> read. --- capnp_conv_derive/src/lib.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 4e2a48c57..b8f984cb5 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -19,7 +19,7 @@ use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident}; -fn gen_into_capnp_named_struct( +fn gen_write_capnp_named_struct( fields_named: &FieldsNamed, rust_struct: &Ident, capnp_struct: &Ident, @@ -44,7 +44,7 @@ fn gen_into_capnp_named_struct( } } -fn gen_from_capnp_named_struct( +fn gen_read_capnp_named_struct( fields_named: &FieldsNamed, rust_struct: &Ident, capnp_struct: &Ident, @@ -70,7 +70,7 @@ fn gen_from_capnp_named_struct( } } -fn gen_into_capnp_enum( +fn gen_write_capnp_enum( _data_enum: &DataEnum, _rust_struct: &Ident, _capnp_struct: &Ident, @@ -78,7 +78,7 @@ fn gen_into_capnp_enum( unimplemented!(); } -fn gen_from_capnp_enum( +fn gen_read_capnp_enum( _data_enum: &DataEnum, _rust_struct: &Ident, _capnp_struct: &Ident, @@ -110,14 +110,14 @@ pub fn capnp_conv( // x: u32, // y: u32, // } - let into_capnp = - gen_into_capnp_named_struct(fields_named, rust_struct, &capnp_struct); - let from_capnp = - gen_from_capnp_named_struct(fields_named, rust_struct, &capnp_struct); + let write_capnp = + gen_write_capnp_named_struct(fields_named, rust_struct, &capnp_struct); + let read_capnp = + gen_read_capnp_named_struct(fields_named, rust_struct, &capnp_struct); quote! { - #into_capnp - #from_capnp + #write_capnp + #read_capnp } } Fields::Unnamed(_) | Fields::Unit => unimplemented!(), @@ -129,12 +129,12 @@ pub fn capnp_conv( // Type2, // Type3(MyStruct), // } - let into_capnp = gen_into_capnp_enum(data_enum, rust_struct, &capnp_struct); - let from_capnp = gen_from_capnp_enum(data_enum, rust_struct, &capnp_struct); + let write_capnp = gen_write_capnp_enum(data_enum, rust_struct, &capnp_struct); + let read_capnp = gen_read_capnp_enum(data_enum, rust_struct, &capnp_struct); quote! { - #into_capnp - #from_capnp + #write_capnp + #read_capnp } } Data::Union(_) => unimplemented!(), From 3ee502dfa8ba3b31a50476b2e8136f9b25160a28 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 17:56:11 +0300 Subject: [PATCH 05/50] Some work on offst-capnp-conv. Currently can serialize basic capnp structs. --- Cargo.toml | 6 +-- build.rs | 7 ++++ capnp_conv_derive/src/lib.rs | 80 +++++++++++++++++++++++++---------- src/lib.rs | 10 +++-- tests/capnp/test.capnp | 22 ++++++++++ tests/derive.rs | 81 ++++++++++++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 28 deletions(-) create mode 100644 build.rs create mode 100644 tests/capnp/test.capnp create mode 100644 tests/derive.rs diff --git a/Cargo.toml b/Cargo.toml index 7f81eceab..99efb8bef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,10 @@ edition = "2018" log = "0.4" pretty_env_logger = "0.2" -futures-preview = "0.3.0-alpha.16" +capnp_conv_derive = { path = "capnp_conv_derive", version = "0.1.0", package = "offst-capnp-conv-derive" } -byteorder = "1.1" capnp = "0.10.0" derive_more = "0.15.0" - +[build-dependencies] +capnpc = "0.10.0" diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..1ef2c362e --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +fn main() { + capnpc::CompilerCommand::new() + .src_prefix("tests/") + .file("tests/capnp/test.capnp") + .run() + .unwrap(); +} diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index b8f984cb5..df84801eb 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -17,27 +17,58 @@ use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident}; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident, Path}; + +fn is_primitive_type(ty: &syn::Type) -> bool { + match ty { + syn::Type::Path(type_path) => { + // TODO: What is qself? + if type_path.qself.is_some() { + return false; + } + + let path = &type_path.path; + path.is_ident("u8") + || path.is_ident("u16") + || path.is_ident("u32") + || path.is_ident("u64") + || path.is_ident("i8") + || path.is_ident("i16") + || path.is_ident("i32") + || path.is_ident("i64") + || path.is_ident("bool") + } + _ => false, + } +} fn gen_write_capnp_named_struct( fields_named: &FieldsNamed, rust_struct: &Ident, - capnp_struct: &Ident, + capnp_struct: &Path, ) -> TokenStream { - // let capnp_write = unimplemented!(); - let recurse = fields_named.named.iter().map(|f| { - let name = &f.ident; - quote_spanned! {f.span() => - self.#name.write_capnp(&mut writer.reborrow().init_#name()); + let name = &f.ident.as_ref().unwrap(); + // let init_method_str = format!("init_", ident); + + if is_primitive_type(&f.ty) { + let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); + quote_spanned! {f.span() => + writer.reborrow().#set_method(self.#name); + } + } else { + let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); + quote_spanned! {f.span() => + self.#name.write_capnp(&mut writer.reborrow().#init_method()); + } } }); quote! { - impl CapnpWriter for #rust_struct { - type WriterType = #capnp_struct::Writer; + impl<'a> WriteCapnp<'a> for #rust_struct { + type WriterType = #capnp_struct::Builder<'a>; - fn write_capnp(self, writer: &mut Self::WriterType) { + fn write_capnp(&'a self, writer: &'a mut Self::WriterType) { #(#recurse)* } } @@ -47,21 +78,28 @@ fn gen_write_capnp_named_struct( fn gen_read_capnp_named_struct( fields_named: &FieldsNamed, rust_struct: &Ident, - capnp_struct: &Ident, + capnp_struct: &Path, ) -> TokenStream { let recurse = fields_named.named.iter().map(|f| { - let name = &f.ident; - quote_spanned! {f.span() => - // route: deser_friends_route(&route_capacity_rate_reader.get_route()?)?, - #name: TODO_TYPE_NAME::read_capnp(&reader.get_#name()?)? + let name = &f.ident.as_ref().unwrap(); + let ty = &f.ty; + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + if is_primitive_type(ty) { + quote_spanned! {f.span() => + #name: reader.#get_method() + } + } else { + quote_spanned! {f.span() => + #name: #ty::read_capnp(&reader.#get_method()?)? + } } }); quote! { - impl CapnpReader for #rust_struct { - type ReaderType = #capnp_struct::Reader; + impl<'a> ReadCapnp<'a> for #rust_struct { + type ReaderType = #capnp_struct::Reader<'a>; - fn read_capnp(reader: &mut Self::ReaderType) { + fn read_capnp(reader: &'a Self::ReaderType) -> Result { Ok(#rust_struct { #(#recurse,)* }) @@ -73,7 +111,7 @@ fn gen_read_capnp_named_struct( fn gen_write_capnp_enum( _data_enum: &DataEnum, _rust_struct: &Ident, - _capnp_struct: &Ident, + _capnp_struct: &Path, ) -> TokenStream { unimplemented!(); } @@ -81,7 +119,7 @@ fn gen_write_capnp_enum( fn gen_read_capnp_enum( _data_enum: &DataEnum, _rust_struct: &Ident, - _capnp_struct: &Ident, + _capnp_struct: &Path, ) -> TokenStream { unimplemented!(); } @@ -96,7 +134,7 @@ pub fn capnp_conv( // for information about arguments. // Name of capnp struct: - let capnp_struct = parse_macro_input!(args as Ident); + let capnp_struct = parse_macro_input!(args as Path); let input = parse_macro_input!(input as DeriveInput); // Name of local struct: diff --git a/src/lib.rs b/src/lib.rs index 7b1e64ea1..b74252eb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,8 @@ use capnp; use derive_more::From; +pub use capnp_conv_derive::capnp_conv; + #[derive(Debug, From)] pub enum CapnpConvError { CapnpError(capnp::Error), @@ -26,19 +28,19 @@ pub enum CapnpConvError { } /// Convert Rust struct to Capnp. -pub trait WriteCapnp { +pub trait WriteCapnp<'a> { /// The corresponding Capnp writer type. type WriterType; /// Converts a Rust struct to corresponding Capnp struct. This should not fail. - fn write_capnp(self, writer: &mut Self::WriterType); + fn write_capnp(&'a self, writer: &'a mut Self::WriterType); } /// Convert Capnp struct to Rust. -pub trait ReadCapnp: Sized { +pub trait ReadCapnp<'a>: Sized { /// The corresponding Capnp reader type. type ReaderType; /// Converts a Capnp struct to corresponding Rust struct. - fn read_capnp(reader: &Self::ReaderType) -> Result; + fn read_capnp(reader: &'a Self::ReaderType) -> Result; } diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp new file mode 100644 index 000000000..50b487542 --- /dev/null +++ b/tests/capnp/test.capnp @@ -0,0 +1,22 @@ +@0xc90daeac68e62b2a; + +# struct TestStructInner { +# innerU8 @0: UInt8; +# } + +struct TestStruct { + myBool @0: Bool; + myInt8 @1: Int8; + myInt16 @2: Int16; + myInt32 @3: Int32; + myInt64 @4: Int64; + myUint8 @5: UInt8; + myUint16 @6: UInt16; + myUint32 @7: UInt32; + myUint64 @8: UInt64; + # my_float32: f32, + # my_float64: f64, + # myText @9: Text; + # myData @10: Data; + # myList @11: List(TestStructInner); +} diff --git a/tests/derive.rs b/tests/derive.rs new file mode 100644 index 000000000..89341d19b --- /dev/null +++ b/tests/derive.rs @@ -0,0 +1,81 @@ +#![deny(warnings)] + +use std::io; + +use capnp::{self, serialize_packed}; +use offst_capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; + +#[allow(unused)] +mod test_capnp { + include!(concat!(env!("OUT_DIR"), "/capnp/test_capnp.rs")); +} + +/* +#[allow(unused)] +#[capnp_conv(test_capnp::test_struct_inner)] +#[derive(Debug, Clone, PartialEq)] +struct TestStructInner { + inner_u8: u8, +} +*/ + +#[allow(unused)] +#[capnp_conv(test_capnp::test_struct)] +#[derive(Debug, Clone, PartialEq)] +struct TestStruct { + my_bool: bool, + my_int8: i8, + my_int16: i16, + my_int32: i32, + my_int64: i64, + my_uint8: u8, + my_uint16: u16, + my_uint32: u32, + my_uint64: u64, + // my_float32: f32, + // my_float64: f64, + // my_text: String, + // my_data: Vec, + // my_list: Vec, +} + +#[test] +fn capnp_serialize_basic_struct() { + // Serialize: + let test_struct = TestStruct { + my_bool: true, + my_int8: -1i8, + my_int16: 1i16, + my_int32: -1i32, + my_int64: 1i64, + my_uint8: 1u8, + my_uint16: 2u16, + my_uint32: 3u32, + my_uint64: 4u64, + // my_float32: -0.5f32, + // my_float64: 0.5f64, + // my_text: "my_text".to_owned(), + // my_data: vec![1, 2, 3, 4, 5u8], + // my_list: vec![TestStructInner { inner_u8: 1u8 }], + }; + + let mut builder = capnp::message::Builder::new_default(); + let mut test_struct_builder = builder.init_root::(); + + test_struct.write_capnp(&mut test_struct_builder); + + let mut data = Vec::new(); + serialize_packed::write_message(&mut data, &builder).unwrap(); + + // Deserialize: + let mut cursor = io::Cursor::new(&data); + let reader = + serialize_packed::read_message(&mut cursor, capnp::message::ReaderOptions::new()).unwrap(); + let test_struct_reader = reader + .get_root::() + .unwrap(); + + let test_struct2 = TestStruct::read_capnp(&test_struct_reader).unwrap(); + + assert_eq!(test_struct, test_struct2); +} From 5cc103691cded81516415606f859c986caf8f21c Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 18:32:30 +0300 Subject: [PATCH 06/50] Added String support for capnp conversion. --- capnp_conv_derive/src/lib.rs | 108 +++++++++++++++++++++++------------ src/lib.rs | 18 ++++++ tests/capnp/test.capnp | 2 +- tests/derive.rs | 4 +- 4 files changed, 92 insertions(+), 40 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index df84801eb..ad8a11585 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -19,16 +19,16 @@ use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident, Path}; -fn is_primitive_type(ty: &syn::Type) -> bool { - match ty { +fn gen_type_write(field: &syn::Field) -> TokenStream { + match &field.ty { syn::Type::Path(type_path) => { - // TODO: What is qself? if type_path.qself.is_some() { - return false; + // Self qualifier? + unimplemented!(); } let path = &type_path.path; - path.is_ident("u8") + let is_primitive = path.is_ident("u8") || path.is_ident("u16") || path.is_ident("u32") || path.is_ident("u64") @@ -36,9 +36,68 @@ fn is_primitive_type(ty: &syn::Type) -> bool { || path.is_ident("i16") || path.is_ident("i32") || path.is_ident("i64") - || path.is_ident("bool") + || path.is_ident("bool"); + + let name = &field.ident.as_ref().unwrap(); + + if is_primitive { + let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); + return quote_spanned! {field.span() => + writer.reborrow().#set_method(self.#name); + }; + } + + if path.is_ident("String") { + let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); + return quote_spanned! {field.span() => + writer.reborrow().#set_method(&self.#name); + }; + } + + unimplemented!(); + } + _ => unimplemented!(), + } +} + +fn gen_type_read(field: &syn::Field) -> TokenStream { + match &field.ty { + syn::Type::Path(type_path) => { + if type_path.qself.is_some() { + // Self qualifier? + unimplemented!(); + } + + let path = &type_path.path; + let is_primitive = path.is_ident("u8") + || path.is_ident("u16") + || path.is_ident("u32") + || path.is_ident("u64") + || path.is_ident("i8") + || path.is_ident("i16") + || path.is_ident("i32") + || path.is_ident("i64") + || path.is_ident("bool"); + + let name = &field.ident.as_ref().unwrap(); + + if is_primitive { + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + return quote_spanned! {field.span() => + #name: reader.#get_method() + }; + } + + if path.is_ident("String") { + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + return quote_spanned! {field.span() => + #name: reader.#get_method()?.to_string() + }; + } + + unimplemented!(); } - _ => false, + _ => unimplemented!(), } } @@ -47,22 +106,10 @@ fn gen_write_capnp_named_struct( rust_struct: &Ident, capnp_struct: &Path, ) -> TokenStream { - let recurse = fields_named.named.iter().map(|f| { - let name = &f.ident.as_ref().unwrap(); - // let init_method_str = format!("init_", ident); - - if is_primitive_type(&f.ty) { - let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); - quote_spanned! {f.span() => - writer.reborrow().#set_method(self.#name); - } - } else { - let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); - quote_spanned! {f.span() => - self.#name.write_capnp(&mut writer.reborrow().#init_method()); - } - } - }); + let recurse = fields_named + .named + .iter() + .map(|field| gen_type_write(&field)); quote! { impl<'a> WriteCapnp<'a> for #rust_struct { @@ -80,20 +127,7 @@ fn gen_read_capnp_named_struct( rust_struct: &Ident, capnp_struct: &Path, ) -> TokenStream { - let recurse = fields_named.named.iter().map(|f| { - let name = &f.ident.as_ref().unwrap(); - let ty = &f.ty; - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - if is_primitive_type(ty) { - quote_spanned! {f.span() => - #name: reader.#get_method() - } - } else { - quote_spanned! {f.span() => - #name: #ty::read_capnp(&reader.#get_method()?)? - } - } - }); + let recurse = fields_named.named.iter().map(|field| gen_type_read(field)); quote! { impl<'a> ReadCapnp<'a> for #rust_struct { diff --git a/src/lib.rs b/src/lib.rs index b74252eb6..f526139e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,3 +44,21 @@ pub trait ReadCapnp<'a>: Sized { /// Converts a Capnp struct to corresponding Rust struct. fn read_capnp(reader: &'a Self::ReaderType) -> Result; } + +// String implementation: +impl<'a> WriteCapnp<'a> for String { + type WriterType = capnp::text::Builder<'a>; + + fn write_capnp(&'a self, writer: &'a mut Self::WriterType) { + writer.push_str(&self); + } +} + +impl<'a> ReadCapnp<'a> for String { + type ReaderType = capnp::text::Reader<'a>; + + fn read_capnp(reader: &'a Self::ReaderType) -> Result { + // A text reader is actually a &str: + Ok(reader.to_string()) + } +} diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 50b487542..6366ff646 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -16,7 +16,7 @@ struct TestStruct { myUint64 @8: UInt64; # my_float32: f32, # my_float64: f64, - # myText @9: Text; + myText @9: Text; # myData @10: Data; # myList @11: List(TestStructInner); } diff --git a/tests/derive.rs b/tests/derive.rs index 89341d19b..57d899ad2 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -34,7 +34,7 @@ struct TestStruct { my_uint64: u64, // my_float32: f32, // my_float64: f64, - // my_text: String, + my_text: String, // my_data: Vec, // my_list: Vec, } @@ -54,7 +54,7 @@ fn capnp_serialize_basic_struct() { my_uint64: 4u64, // my_float32: -0.5f32, // my_float64: 0.5f64, - // my_text: "my_text".to_owned(), + my_text: "my_text".to_owned(), // my_data: vec![1, 2, 3, 4, 5u8], // my_list: vec![TestStructInner { inner_u8: 1u8 }], }; From c67bc994cac145fd3b84d90ca285fc157ad979aa Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 18:43:08 +0300 Subject: [PATCH 07/50] Added nested struct support for capnp serialization. --- capnp_conv_derive/src/lib.rs | 12 ++++++++++-- tests/capnp/test.capnp | 7 ++++--- tests/derive.rs | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index ad8a11585..cb4d143b4 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -54,7 +54,11 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { }; } - unimplemented!(); + // Generic type: + let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); + quote_spanned! {field.span() => + self.#name.write_capnp(&mut writer.reborrow().#init_method()); + } } _ => unimplemented!(), } @@ -95,7 +99,11 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { }; } - unimplemented!(); + // Generic type: + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + quote_spanned! {field.span() => + #name: #type_path::read_capnp(&reader.#get_method()?)? + } } _ => unimplemented!(), } diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 6366ff646..f5c9bf796 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -1,8 +1,8 @@ @0xc90daeac68e62b2a; -# struct TestStructInner { -# innerU8 @0: UInt8; -# } +struct TestStructInner { + innerU8 @0: UInt8; +} struct TestStruct { myBool @0: Bool; @@ -17,6 +17,7 @@ struct TestStruct { # my_float32: f32, # my_float64: f64, myText @9: Text; + structInner @10: TestStructInner; # myData @10: Data; # myList @11: List(TestStructInner); } diff --git a/tests/derive.rs b/tests/derive.rs index 57d899ad2..3be901aaf 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -10,14 +10,12 @@ mod test_capnp { include!(concat!(env!("OUT_DIR"), "/capnp/test_capnp.rs")); } -/* #[allow(unused)] #[capnp_conv(test_capnp::test_struct_inner)] #[derive(Debug, Clone, PartialEq)] struct TestStructInner { inner_u8: u8, } -*/ #[allow(unused)] #[capnp_conv(test_capnp::test_struct)] @@ -35,6 +33,7 @@ struct TestStruct { // my_float32: f32, // my_float64: f64, my_text: String, + struct_inner: TestStructInner, // my_data: Vec, // my_list: Vec, } @@ -55,6 +54,7 @@ fn capnp_serialize_basic_struct() { // my_float32: -0.5f32, // my_float64: 0.5f64, my_text: "my_text".to_owned(), + struct_inner: TestStructInner { inner_u8: 1u8 }, // my_data: vec![1, 2, 3, 4, 5u8], // my_list: vec![TestStructInner { inner_u8: 1u8 }], }; From 84c72fcadc38961e5c6b60f8d3658e6bf399bbd1 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 19:19:09 +0300 Subject: [PATCH 08/50] offst-capnp-conv: Added support for Data --- capnp_conv_derive/src/lib.rs | 144 ++++++++++++++++++++++++++++++----- tests/capnp/test.capnp | 4 +- tests/derive.rs | 4 +- 3 files changed, 128 insertions(+), 24 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index cb4d143b4..533d7578d 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -10,6 +10,7 @@ clippy::module_inception, clippy::new_without_default )] +#![allow(unreachable_code)] extern crate proc_macro; @@ -19,6 +20,103 @@ use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident, Path}; +/// Is a primitive type? +fn is_primitive(path: &syn::Path) -> bool { + path.is_ident("u8") + || path.is_ident("u16") + || path.is_ident("u32") + || path.is_ident("u64") + || path.is_ident("i8") + || path.is_ident("i16") + || path.is_ident("i32") + || path.is_ident("i64") + || path.is_ident("bool") +} + +/// Check if the path represents a Vec +fn is_data(path: &syn::Path) -> bool { + let last_segment = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_ident) => last_ident, + _ => unreachable!(), + }; + if &last_segment.ident.to_string() != "Vec" { + return false; + } + let angle = match &last_segment.arguments { + syn::PathArguments::AngleBracketed(angle) => { + if angle.args.len() > 1 { + unreachable!("Too many arguments for Vec!"); + } + angle + } + _ => unreachable!("Vec with arguments that are not angle bracketed!"), + }; + let last_arg = match angle.args.last().unwrap() { + syn::punctuated::Pair::End(last_arg) => last_arg, + _ => return false, + }; + + let arg_ty = match last_arg { + syn::GenericArgument::Type(arg_ty) => arg_ty, + _ => return false, + }; + + let arg_ty_path = match arg_ty { + syn::Type::Path(arg_ty_path) => arg_ty_path, + _ => return false, + }; + + if !arg_ty_path.path.is_ident("u8") { + return false; + } + + true +} + +/// Check if the path represents a Vec, where SomeStruct != u8 +fn is_list(path: &syn::Path) -> bool { + if !path.is_ident("Vec") { + return false; + } + // Could be a List, or Data. + // If this is Vec, we decide that it is Data. Otherwise we decide it is a List. + // + let last_ident = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_ident) => last_ident, + _ => unreachable!(), + }; + + let angle = match &last_ident.arguments { + syn::PathArguments::AngleBracketed(angle) => { + if angle.args.len() > 1 { + unreachable!("Too many arguments for Vec!"); + } + angle + } + _ => unreachable!("Vec with arguments that are not angle bracketed!"), + }; + let last_arg = match angle.args.last().unwrap() { + syn::punctuated::Pair::End(last_arg) => last_arg, + _ => return false, + }; + + let arg_ty = match last_arg { + syn::GenericArgument::Type(arg_ty) => arg_ty, + _ => return false, + }; + + let arg_ty_path = match arg_ty { + syn::Type::Path(arg_ty_path) => arg_ty_path, + _ => return false, + }; + + if arg_ty_path.path.is_ident("u8") { + return false; + } + + true +} + fn gen_type_write(field: &syn::Field) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { @@ -28,19 +126,10 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { } let path = &type_path.path; - let is_primitive = path.is_ident("u8") - || path.is_ident("u16") - || path.is_ident("u32") - || path.is_ident("u64") - || path.is_ident("i8") - || path.is_ident("i16") - || path.is_ident("i32") - || path.is_ident("i64") - || path.is_ident("bool"); let name = &field.ident.as_ref().unwrap(); - if is_primitive { + if is_primitive(path) { let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); return quote_spanned! {field.span() => writer.reborrow().#set_method(self.#name); @@ -54,6 +143,18 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { }; } + if is_data(path) { + let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); + return quote_spanned! {field.span() => + writer.reborrow().#set_method(&self.#name); + }; + } + + if is_list(path) { + // List case: + unimplemented!(); + } + // Generic type: let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); quote_spanned! {field.span() => @@ -73,19 +174,10 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { } let path = &type_path.path; - let is_primitive = path.is_ident("u8") - || path.is_ident("u16") - || path.is_ident("u32") - || path.is_ident("u64") - || path.is_ident("i8") - || path.is_ident("i16") - || path.is_ident("i32") - || path.is_ident("i64") - || path.is_ident("bool"); let name = &field.ident.as_ref().unwrap(); - if is_primitive { + if is_primitive(path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); return quote_spanned! {field.span() => #name: reader.#get_method() @@ -99,6 +191,18 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { }; } + if is_data(path) { + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + return quote_spanned! {field.span() => + #name: reader.#get_method()?.to_vec() + }; + } + + if is_list(path) { + // List case: + unimplemented!(); + } + // Generic type: let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); quote_spanned! {field.span() => diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index f5c9bf796..934e65ad1 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -17,7 +17,7 @@ struct TestStruct { # my_float32: f32, # my_float64: f64, myText @9: Text; - structInner @10: TestStructInner; - # myData @10: Data; + myData @10: Data; + structInner @11: TestStructInner; # myList @11: List(TestStructInner); } diff --git a/tests/derive.rs b/tests/derive.rs index 3be901aaf..25ae0bd6c 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -33,8 +33,8 @@ struct TestStruct { // my_float32: f32, // my_float64: f64, my_text: String, + my_data: Vec, struct_inner: TestStructInner, - // my_data: Vec, // my_list: Vec, } @@ -54,8 +54,8 @@ fn capnp_serialize_basic_struct() { // my_float32: -0.5f32, // my_float64: 0.5f64, my_text: "my_text".to_owned(), + my_data: vec![1, 2, 3, 4, 5u8], struct_inner: TestStructInner { inner_u8: 1u8 }, - // my_data: vec![1, 2, 3, 4, 5u8], // my_list: vec![TestStructInner { inner_u8: 1u8 }], }; From 503007f940409250ad8e1bb12febcf22ed1f15d1 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 19:30:19 +0300 Subject: [PATCH 09/50] Removed unused comment. --- src/lib.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f526139e0..b74252eb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,21 +44,3 @@ pub trait ReadCapnp<'a>: Sized { /// Converts a Capnp struct to corresponding Rust struct. fn read_capnp(reader: &'a Self::ReaderType) -> Result; } - -// String implementation: -impl<'a> WriteCapnp<'a> for String { - type WriterType = capnp::text::Builder<'a>; - - fn write_capnp(&'a self, writer: &'a mut Self::WriterType) { - writer.push_str(&self); - } -} - -impl<'a> ReadCapnp<'a> for String { - type ReaderType = capnp::text::Reader<'a>; - - fn read_capnp(reader: &'a Self::ReaderType) -> Result { - // A text reader is actually a &str: - Ok(reader.to_string()) - } -} From 7297d701fc5a9cf86cec392590935b8b694e78e4 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 29 Jun 2019 20:28:43 +0300 Subject: [PATCH 10/50] capnp_conv: Began working on List case. Not working yet. --- capnp_conv_derive/src/lib.rs | 41 ++++++++++++++++++++++++------------ tests/capnp/test.capnp | 2 +- tests/derive.rs | 8 +++++-- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 533d7578d..47d386422 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -36,7 +36,7 @@ fn is_primitive(path: &syn::Path) -> bool { /// Check if the path represents a Vec fn is_data(path: &syn::Path) -> bool { let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_ident) => last_ident, + syn::punctuated::Pair::End(last_segment) => last_segment, _ => unreachable!(), }; if &last_segment.ident.to_string() != "Vec" { @@ -75,18 +75,14 @@ fn is_data(path: &syn::Path) -> bool { /// Check if the path represents a Vec, where SomeStruct != u8 fn is_list(path: &syn::Path) -> bool { - if !path.is_ident("Vec") { - return false; - } - // Could be a List, or Data. - // If this is Vec, we decide that it is Data. Otherwise we decide it is a List. - // - let last_ident = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_ident) => last_ident, + let last_segment = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_segment) => last_segment, _ => unreachable!(), }; - - let angle = match &last_ident.arguments { + if &last_segment.ident.to_string() != "Vec" { + return false; + } + let angle = match &last_segment.arguments { syn::PathArguments::AngleBracketed(angle) => { if angle.args.len() > 1 { unreachable!("Too many arguments for Vec!"); @@ -110,6 +106,7 @@ fn is_list(path: &syn::Path) -> bool { _ => return false, }; + // Make sure that we don't deal with Vec: if arg_ty_path.path.is_ident("u8") { return false; } @@ -151,8 +148,19 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { } if is_list(path) { - // List case: - unimplemented!(); + let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); + return quote_spanned! {field.span() => + let mut list_builder = writer + .reborrow() + .#init_method(usize_to_u32(self.#name.len()).unwrap()); + + for (index, item) in self.#name.iter().enumerate() { + let mut item_builder = list_builder + .reborrow() + .get(usize_to_u32(index).unwrap()); + item_builder.write_capnp(&mut item_builder); + } + }; } // Generic type: @@ -325,6 +333,13 @@ pub fn capnp_conv( }; let expanded = quote! { + pub fn usize_to_u32(num: usize) -> Option { + if num > std::u32::MAX as usize { + None + } else { + Some(num as u32) + } + } // Original structure #input // Generated mutual From conversion code: diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 934e65ad1..874223a78 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -19,5 +19,5 @@ struct TestStruct { myText @9: Text; myData @10: Data; structInner @11: TestStructInner; - # myList @11: List(TestStructInner); + myList @12: List(TestStructInner); } diff --git a/tests/derive.rs b/tests/derive.rs index 25ae0bd6c..1964dd5c6 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -35,7 +35,7 @@ struct TestStruct { my_text: String, my_data: Vec, struct_inner: TestStructInner, - // my_list: Vec, + my_list: Vec, } #[test] @@ -56,7 +56,11 @@ fn capnp_serialize_basic_struct() { my_text: "my_text".to_owned(), my_data: vec![1, 2, 3, 4, 5u8], struct_inner: TestStructInner { inner_u8: 1u8 }, - // my_list: vec![TestStructInner { inner_u8: 1u8 }], + my_list: vec![ + TestStructInner { inner_u8: 2u8 }, + TestStructInner { inner_u8: 3u8 }, + TestStructInner { inner_u8: 4u8 }, + ], }; let mut builder = capnp::message::Builder::new_default(); From c46a20b4395ee3b5739e660e08ec14bbc1bd12ca Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 11:53:34 +0300 Subject: [PATCH 11/50] Initial implementation for list (primitive + non-primitive) serialization. --- capnp_conv_derive/src/lib.rs | 120 +++++++++++++++++++++++------------ tests/capnp/test.capnp | 3 +- tests/derive.rs | 3 + 3 files changed, 86 insertions(+), 40 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 47d386422..29f761c1e 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -66,6 +66,10 @@ fn is_data(path: &syn::Path) -> bool { _ => return false, }; + if arg_ty_path.qself.is_some() { + return false; + } + if !arg_ty_path.path.is_ident("u8") { return false; } @@ -74,13 +78,13 @@ fn is_data(path: &syn::Path) -> bool { } /// Check if the path represents a Vec, where SomeStruct != u8 -fn is_list(path: &syn::Path) -> bool { +fn get_list(path: &syn::Path) -> Option { let last_segment = match path.segments.last().unwrap() { syn::punctuated::Pair::End(last_segment) => last_segment, _ => unreachable!(), }; if &last_segment.ident.to_string() != "Vec" { - return false; + return None; } let angle = match &last_segment.arguments { syn::PathArguments::AngleBracketed(angle) => { @@ -93,25 +97,51 @@ fn is_list(path: &syn::Path) -> bool { }; let last_arg = match angle.args.last().unwrap() { syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return false, + _ => return None, }; let arg_ty = match last_arg { syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return false, + _ => return None, }; let arg_ty_path = match arg_ty { syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return false, + _ => return None, }; + if arg_ty_path.qself.is_some() { + return None; + } + // Make sure that we don't deal with Vec: if arg_ty_path.path.is_ident("u8") { - return false; + return None; } - true + Some(arg_ty_path.path.clone()) +} + +fn gen_list_write_iter(path: &syn::Path) -> TokenStream { + if is_primitive(path) || path.is_ident("String") || is_data(path) { + // A primitive list: + quote! { + list_builder + .reborrow() + .set(u32::try_from(index).unwrap(), item.clone()); + } + } else { + // Not a primitive list: + quote! { + let mut item_builder = list_builder + .reborrow() + .get(u32::try_from(index).unwrap()); + + item.write_capnp(&mut item_builder); + } + } + // TODO: It seems like we do not support List(List(...)) at the moment. + // How to support it? } fn gen_type_write(field: &syn::Field) -> TokenStream { @@ -133,32 +163,32 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { }; } - if path.is_ident("String") { + if path.is_ident("String") || is_data(path) { let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); return quote_spanned! {field.span() => writer.reborrow().#set_method(&self.#name); }; } - if is_data(path) { - let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); - return quote_spanned! {field.span() => - writer.reborrow().#set_method(&self.#name); - }; - } - - if is_list(path) { + if let Some(inner_path) = get_list(path) { let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); + let list_write_iter = gen_list_write_iter(&inner_path); + + // In the cases of more complicated types, list_builder needs to be mutable. + let let_list_builder = + if is_primitive(path) || path.is_ident("String") || is_data(path) { + quote! { let list_builder } + } else { + quote! { let mut list_builder } + }; + return quote_spanned! {field.span() => - let mut list_builder = writer + #let_list_builder = writer .reborrow() - .#init_method(usize_to_u32(self.#name.len()).unwrap()); + .#init_method(u32::try_from(self.#name.len()).unwrap()); for (index, item) in self.#name.iter().enumerate() { - let mut item_builder = list_builder - .reborrow() - .get(usize_to_u32(index).unwrap()); - item_builder.write_capnp(&mut item_builder); + #list_write_iter } }; } @@ -173,6 +203,22 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { } } +fn gen_list_read_iter(path: &syn::Path) -> TokenStream { + if is_primitive(path) || path.is_ident("String") || is_data(path) { + // A primitive list: + quote! { + res_vec.push(item_reader.into()); + } + } else { + // Not a primitive list: + quote! { + res_vec.push(#path::read_capnp(&item_reader)?); + } + } + // TODO: It seems like we do not support List(List(...)) at the moment. + // How to support it? +} + fn gen_type_read(field: &syn::Field) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { @@ -188,29 +234,32 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { if is_primitive(path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); return quote_spanned! {field.span() => - #name: reader.#get_method() + #name: reader.#get_method().into() }; } - if path.is_ident("String") { + if path.is_ident("String") || is_data(path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); return quote_spanned! {field.span() => - #name: reader.#get_method()?.to_string() + #name: reader.#get_method()?.into() }; } - if is_data(path) { + if let Some(inner_path) = get_list(path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + let list_read_iter = gen_list_read_iter(&inner_path); return quote_spanned! {field.span() => - #name: reader.#get_method()?.to_vec() + #name: { + let mut res_vec = Vec::new(); + for item_reader in reader.#get_method()? { + // res_vec.push_back(read_named_relay_address(&named_relay_address)?); + #list_read_iter + } + res_vec + } }; } - if is_list(path) { - // List case: - unimplemented!(); - } - // Generic type: let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); quote_spanned! {field.span() => @@ -333,13 +382,6 @@ pub fn capnp_conv( }; let expanded = quote! { - pub fn usize_to_u32(num: usize) -> Option { - if num > std::u32::MAX as usize { - None - } else { - Some(num as u32) - } - } // Original structure #input // Generated mutual From conversion code: diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 874223a78..b5cfd95f4 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -19,5 +19,6 @@ struct TestStruct { myText @9: Text; myData @10: Data; structInner @11: TestStructInner; - myList @12: List(TestStructInner); + myPrimitiveList @12: List(UInt16); + myList @13: List(TestStructInner); } diff --git a/tests/derive.rs b/tests/derive.rs index 1964dd5c6..a8ffab7ec 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,5 +1,6 @@ #![deny(warnings)] +use std::convert::TryFrom; use std::io; use capnp::{self, serialize_packed}; @@ -35,6 +36,7 @@ struct TestStruct { my_text: String, my_data: Vec, struct_inner: TestStructInner, + my_primitive_list: Vec, my_list: Vec, } @@ -56,6 +58,7 @@ fn capnp_serialize_basic_struct() { my_text: "my_text".to_owned(), my_data: vec![1, 2, 3, 4, 5u8], struct_inner: TestStructInner { inner_u8: 1u8 }, + my_primitive_list: vec![10, 11, 12, 13, 14u16], my_list: vec![ TestStructInner { inner_u8: 2u8 }, TestStructInner { inner_u8: 3u8 }, From c8ee7c54c11502e00305a7b55f0b8392c30a728a Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 13:50:41 +0300 Subject: [PATCH 12/50] Added IntoCapnpBytes and FromCapnpBytes traits --- capnp_conv_derive/src/lib.rs | 4 +-- src/lib.rs | 55 +++++++++++++++++++++++++++++++++--- tests/derive.rs | 28 +++++------------- 3 files changed, 60 insertions(+), 27 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 29f761c1e..ab628a113 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -284,7 +284,7 @@ fn gen_write_capnp_named_struct( impl<'a> WriteCapnp<'a> for #rust_struct { type WriterType = #capnp_struct::Builder<'a>; - fn write_capnp(&'a self, writer: &'a mut Self::WriterType) { + fn write_capnp(&self, writer: &mut Self::WriterType) { #(#recurse)* } } @@ -302,7 +302,7 @@ fn gen_read_capnp_named_struct( impl<'a> ReadCapnp<'a> for #rust_struct { type ReaderType = #capnp_struct::Reader<'a>; - fn read_capnp(reader: &'a Self::ReaderType) -> Result { + fn read_capnp(reader: &Self::ReaderType) -> Result { Ok(#rust_struct { #(#recurse,)* }) diff --git a/src/lib.rs b/src/lib.rs index b74252eb6..341512d1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,17 +30,64 @@ pub enum CapnpConvError { /// Convert Rust struct to Capnp. pub trait WriteCapnp<'a> { /// The corresponding Capnp writer type. - type WriterType; + // type WriterType: capnp::traits::FromPointerBuilder<'a>; + type WriterType: capnp::traits::FromPointerBuilder<'a>; /// Converts a Rust struct to corresponding Capnp struct. This should not fail. - fn write_capnp(&'a self, writer: &'a mut Self::WriterType); + fn write_capnp(&self, writer: &mut Self::WriterType); } /// Convert Capnp struct to Rust. pub trait ReadCapnp<'a>: Sized { /// The corresponding Capnp reader type. - type ReaderType; + type ReaderType: capnp::traits::FromPointerReader<'a>; /// Converts a Capnp struct to corresponding Rust struct. - fn read_capnp(reader: &'a Self::ReaderType) -> Result; + fn read_capnp(reader: &Self::ReaderType) -> Result; +} + +pub trait IntoCapnpBytes { + fn into_capnp_bytes(self) -> Result, CapnpConvError>; +} + +pub trait FromCapnpBytes: Sized { + fn from_capnp_bytes(bytes: &[u8]) -> Result; +} + +impl IntoCapnpBytes for T +where + T: for<'a> WriteCapnp<'a>, +{ + #[allow(unused)] + fn into_capnp_bytes(self) -> Result, CapnpConvError> { + let mut builder = capnp::message::Builder::new_default(); + + // A trick to avoid borrow checker issues: + { + let mut struct_builder = builder.init_root::(); + self.write_capnp(&mut struct_builder); + } + + let mut data = Vec::new(); + capnp::serialize_packed::write_message(&mut data, &builder)?; + Ok(data) + } +} + +impl FromCapnpBytes for T +where + T: for<'a> ReadCapnp<'a>, + // F: capnp::traits::FromPointerReader<'a>, +{ + fn from_capnp_bytes(bytes: &[u8]) -> Result { + // Deserialize: + let mut cursor = io::Cursor::new(&bytes); + let reader = capnp::serialize_packed::read_message( + &mut cursor, + capnp::message::ReaderOptions::new(), + ) + .unwrap(); + let struct_reader = reader.get_root::()?; + Ok(Self::read_capnp(&struct_reader)?) + } } diff --git a/tests/derive.rs b/tests/derive.rs index a8ffab7ec..93372c2a0 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,10 +1,12 @@ #![deny(warnings)] use std::convert::TryFrom; -use std::io; +// use std::io; -use capnp::{self, serialize_packed}; -use offst_capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; +// use capnp; +use offst_capnp_conv::{ + capnp_conv, CapnpConvError, FromCapnpBytes, IntoCapnpBytes, ReadCapnp, WriteCapnp, +}; #[allow(unused)] mod test_capnp { @@ -18,7 +20,6 @@ struct TestStructInner { inner_u8: u8, } -#[allow(unused)] #[capnp_conv(test_capnp::test_struct)] #[derive(Debug, Clone, PartialEq)] struct TestStruct { @@ -66,23 +67,8 @@ fn capnp_serialize_basic_struct() { ], }; - let mut builder = capnp::message::Builder::new_default(); - let mut test_struct_builder = builder.init_root::(); - - test_struct.write_capnp(&mut test_struct_builder); - - let mut data = Vec::new(); - serialize_packed::write_message(&mut data, &builder).unwrap(); - - // Deserialize: - let mut cursor = io::Cursor::new(&data); - let reader = - serialize_packed::read_message(&mut cursor, capnp::message::ReaderOptions::new()).unwrap(); - let test_struct_reader = reader - .get_root::() - .unwrap(); - - let test_struct2 = TestStruct::read_capnp(&test_struct_reader).unwrap(); + let data = test_struct.clone().into_capnp_bytes().unwrap(); + let test_struct2 = TestStruct::from_capnp_bytes(&data).unwrap(); assert_eq!(test_struct, test_struct2); } From 2f0cb1b1c29c1d946dd7acbac6f11149fd0143a0 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 13:53:03 +0300 Subject: [PATCH 13/50] Taking a reference instead of consuming self in to_capnp_bytes(). --- src/lib.rs | 8 ++++---- tests/derive.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 341512d1f..e654ba970 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,20 +46,20 @@ pub trait ReadCapnp<'a>: Sized { fn read_capnp(reader: &Self::ReaderType) -> Result; } -pub trait IntoCapnpBytes { - fn into_capnp_bytes(self) -> Result, CapnpConvError>; +pub trait ToCapnpBytes { + fn to_capnp_bytes(&self) -> Result, CapnpConvError>; } pub trait FromCapnpBytes: Sized { fn from_capnp_bytes(bytes: &[u8]) -> Result; } -impl IntoCapnpBytes for T +impl ToCapnpBytes for T where T: for<'a> WriteCapnp<'a>, { #[allow(unused)] - fn into_capnp_bytes(self) -> Result, CapnpConvError> { + fn to_capnp_bytes(&self) -> Result, CapnpConvError> { let mut builder = capnp::message::Builder::new_default(); // A trick to avoid borrow checker issues: diff --git a/tests/derive.rs b/tests/derive.rs index 93372c2a0..777f55b11 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -5,7 +5,7 @@ use std::convert::TryFrom; // use capnp; use offst_capnp_conv::{ - capnp_conv, CapnpConvError, FromCapnpBytes, IntoCapnpBytes, ReadCapnp, WriteCapnp, + capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, }; #[allow(unused)] @@ -67,7 +67,7 @@ fn capnp_serialize_basic_struct() { ], }; - let data = test_struct.clone().into_capnp_bytes().unwrap(); + let data = test_struct.to_capnp_bytes().unwrap(); let test_struct2 = TestStruct::from_capnp_bytes(&data).unwrap(); assert_eq!(test_struct, test_struct2); From 7b289274281fe1f3b69f19adbadeabc651a172fe Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 13:55:00 +0300 Subject: [PATCH 14/50] Removed unused comments. --- src/lib.rs | 1 - tests/derive.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e654ba970..148aae612 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,7 +77,6 @@ where impl FromCapnpBytes for T where T: for<'a> ReadCapnp<'a>, - // F: capnp::traits::FromPointerReader<'a>, { fn from_capnp_bytes(bytes: &[u8]) -> Result { // Deserialize: diff --git a/tests/derive.rs b/tests/derive.rs index 777f55b11..55fb8e258 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,9 +1,7 @@ #![deny(warnings)] use std::convert::TryFrom; -// use std::io; -// use capnp; use offst_capnp_conv::{ capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, }; From b712124618296de7e5468c814de1f8a569f0b34f Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 13:56:55 +0300 Subject: [PATCH 15/50] Removed allow(unused) hint. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 148aae612..6a8ab16fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,6 @@ impl ToCapnpBytes for T where T: for<'a> WriteCapnp<'a>, { - #[allow(unused)] fn to_capnp_bytes(&self) -> Result, CapnpConvError> { let mut builder = capnp::message::Builder::new_default(); From 11bef7782afecddab62ed28e234277d73e414065 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 13:57:21 +0300 Subject: [PATCH 16/50] Removed old comment. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6a8ab16fb..87faae929 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,6 @@ where T: for<'a> ReadCapnp<'a>, { fn from_capnp_bytes(bytes: &[u8]) -> Result { - // Deserialize: let mut cursor = io::Cursor::new(&bytes); let reader = capnp::serialize_packed::read_message( &mut cursor, From 9720117bc225cb25a4e6ac967f47f8fbc2ebfc6b Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 13:57:28 +0300 Subject: [PATCH 17/50] Added comments. --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 87faae929..5a8fc0d22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,10 +47,12 @@ pub trait ReadCapnp<'a>: Sized { } pub trait ToCapnpBytes { + /// Serialize a Rust struct into bytes using Capnp fn to_capnp_bytes(&self) -> Result, CapnpConvError>; } pub trait FromCapnpBytes: Sized { + /// Deserialize a Rust struct from bytes using Capnp fn from_capnp_bytes(bytes: &[u8]) -> Result; } From c369eac4b1c4c001883384f38cbd22186be5b9e6 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 15:35:39 +0300 Subject: [PATCH 18/50] Replaced unwrap() with error handling. --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5a8fc0d22..6e08db3a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,8 +84,7 @@ where let reader = capnp::serialize_packed::read_message( &mut cursor, capnp::message::ReaderOptions::new(), - ) - .unwrap(); + )?; let struct_reader = reader.get_root::()?; Ok(Self::read_capnp(&struct_reader)?) } From 21888c7cf137f853b005b07ad365f127e2211727 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 15:52:17 +0300 Subject: [PATCH 19/50] Refactored capnp-conv-derive to separate files. --- capnp_conv_derive/src/derive_enum.rs | 20 ++ capnp_conv_derive/src/derive_struct.rs | 296 +++++++++++++++++++++++ capnp_conv_derive/src/lib.rs | 319 +------------------------ 3 files changed, 325 insertions(+), 310 deletions(-) create mode 100644 capnp_conv_derive/src/derive_enum.rs create mode 100644 capnp_conv_derive/src/derive_struct.rs diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs new file mode 100644 index 000000000..90e240aba --- /dev/null +++ b/capnp_conv_derive/src/derive_enum.rs @@ -0,0 +1,20 @@ +use proc_macro2::TokenStream; +// use quote::{quote, quote_spanned}; +// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use syn::{DataEnum, Ident, Path}; + +pub fn gen_write_capnp_enum( + _data_enum: &DataEnum, + _rust_struct: &Ident, + _capnp_struct: &Path, +) -> TokenStream { + unimplemented!(); +} + +pub fn gen_read_capnp_enum( + _data_enum: &DataEnum, + _rust_struct: &Ident, + _capnp_struct: &Path, +) -> TokenStream { + unimplemented!(); +} diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs new file mode 100644 index 000000000..a6ed423f1 --- /dev/null +++ b/capnp_conv_derive/src/derive_struct.rs @@ -0,0 +1,296 @@ +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use syn::{FieldsNamed, Ident, Path}; + +/// Is a primitive type? +fn is_primitive(path: &syn::Path) -> bool { + path.is_ident("u8") + || path.is_ident("u16") + || path.is_ident("u32") + || path.is_ident("u64") + || path.is_ident("i8") + || path.is_ident("i16") + || path.is_ident("i32") + || path.is_ident("i64") + || path.is_ident("bool") +} + +/// Check if the path represents a Vec +fn is_data(path: &syn::Path) -> bool { + let last_segment = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_segment) => last_segment, + _ => unreachable!(), + }; + if &last_segment.ident.to_string() != "Vec" { + return false; + } + let angle = match &last_segment.arguments { + syn::PathArguments::AngleBracketed(angle) => { + if angle.args.len() > 1 { + unreachable!("Too many arguments for Vec!"); + } + angle + } + _ => unreachable!("Vec with arguments that are not angle bracketed!"), + }; + let last_arg = match angle.args.last().unwrap() { + syn::punctuated::Pair::End(last_arg) => last_arg, + _ => return false, + }; + + let arg_ty = match last_arg { + syn::GenericArgument::Type(arg_ty) => arg_ty, + _ => return false, + }; + + let arg_ty_path = match arg_ty { + syn::Type::Path(arg_ty_path) => arg_ty_path, + _ => return false, + }; + + if arg_ty_path.qself.is_some() { + return false; + } + + if !arg_ty_path.path.is_ident("u8") { + return false; + } + + true +} + +/// Check if the path represents a Vec, where SomeStruct != u8 +fn get_list(path: &syn::Path) -> Option { + let last_segment = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_segment) => last_segment, + _ => unreachable!(), + }; + if &last_segment.ident.to_string() != "Vec" { + return None; + } + let angle = match &last_segment.arguments { + syn::PathArguments::AngleBracketed(angle) => { + if angle.args.len() > 1 { + unreachable!("Too many arguments for Vec!"); + } + angle + } + _ => unreachable!("Vec with arguments that are not angle bracketed!"), + }; + let last_arg = match angle.args.last().unwrap() { + syn::punctuated::Pair::End(last_arg) => last_arg, + _ => return None, + }; + + let arg_ty = match last_arg { + syn::GenericArgument::Type(arg_ty) => arg_ty, + _ => return None, + }; + + let arg_ty_path = match arg_ty { + syn::Type::Path(arg_ty_path) => arg_ty_path, + _ => return None, + }; + + if arg_ty_path.qself.is_some() { + return None; + } + + // Make sure that we don't deal with Vec: + if arg_ty_path.path.is_ident("u8") { + return None; + } + + Some(arg_ty_path.path.clone()) +} + +fn gen_list_write_iter(path: &syn::Path) -> TokenStream { + if is_primitive(path) || path.is_ident("String") || is_data(path) { + // A primitive list: + quote! { + list_builder + .reborrow() + .set(u32::try_from(index).unwrap(), item.clone()); + } + } else { + // Not a primitive list: + quote! { + let mut item_builder = list_builder + .reborrow() + .get(u32::try_from(index).unwrap()); + + item.write_capnp(&mut item_builder); + } + } + // TODO: It seems like we do not support List(List(...)) at the moment. + // How to support it? +} + +fn gen_type_write(field: &syn::Field) -> TokenStream { + match &field.ty { + syn::Type::Path(type_path) => { + if type_path.qself.is_some() { + // Self qualifier? + unimplemented!(); + } + + let path = &type_path.path; + + let name = &field.ident.as_ref().unwrap(); + + if is_primitive(path) { + let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); + return quote_spanned! {field.span() => + writer.reborrow().#set_method(self.#name); + }; + } + + if path.is_ident("String") || is_data(path) { + let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); + return quote_spanned! {field.span() => + writer.reborrow().#set_method(&self.#name); + }; + } + + if let Some(inner_path) = get_list(path) { + let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); + let list_write_iter = gen_list_write_iter(&inner_path); + + // In the cases of more complicated types, list_builder needs to be mutable. + let let_list_builder = + if is_primitive(path) || path.is_ident("String") || is_data(path) { + quote! { let list_builder } + } else { + quote! { let mut list_builder } + }; + + return quote_spanned! {field.span() => + #let_list_builder = writer + .reborrow() + .#init_method(u32::try_from(self.#name.len()).unwrap()); + + for (index, item) in self.#name.iter().enumerate() { + #list_write_iter + } + }; + } + + // Generic type: + let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); + quote_spanned! {field.span() => + self.#name.write_capnp(&mut writer.reborrow().#init_method()); + } + } + _ => unimplemented!(), + } +} + +fn gen_list_read_iter(path: &syn::Path) -> TokenStream { + if is_primitive(path) || path.is_ident("String") || is_data(path) { + // A primitive list: + quote! { + res_vec.push(item_reader.into()); + } + } else { + // Not a primitive list: + quote! { + res_vec.push(#path::read_capnp(&item_reader)?); + } + } + // TODO: It seems like we do not support List(List(...)) at the moment. + // How to support it? +} + +fn gen_type_read(field: &syn::Field) -> TokenStream { + match &field.ty { + syn::Type::Path(type_path) => { + if type_path.qself.is_some() { + // Self qualifier? + unimplemented!(); + } + + let path = &type_path.path; + + let name = &field.ident.as_ref().unwrap(); + + if is_primitive(path) { + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + return quote_spanned! {field.span() => + #name: reader.#get_method().into() + }; + } + + if path.is_ident("String") || is_data(path) { + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + return quote_spanned! {field.span() => + #name: reader.#get_method()?.into() + }; + } + + if let Some(inner_path) = get_list(path) { + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + let list_read_iter = gen_list_read_iter(&inner_path); + return quote_spanned! {field.span() => + #name: { + let mut res_vec = Vec::new(); + for item_reader in reader.#get_method()? { + // res_vec.push_back(read_named_relay_address(&named_relay_address)?); + #list_read_iter + } + res_vec + } + }; + } + + // Generic type: + let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + quote_spanned! {field.span() => + #name: #type_path::read_capnp(&reader.#get_method()?)? + } + } + _ => unimplemented!(), + } +} + +pub fn gen_write_capnp_named_struct( + fields_named: &FieldsNamed, + rust_struct: &Ident, + capnp_struct: &Path, +) -> TokenStream { + let recurse = fields_named + .named + .iter() + .map(|field| gen_type_write(&field)); + + quote! { + impl<'a> WriteCapnp<'a> for #rust_struct { + type WriterType = #capnp_struct::Builder<'a>; + + fn write_capnp(&self, writer: &mut Self::WriterType) { + #(#recurse)* + } + } + } +} + +pub fn gen_read_capnp_named_struct( + fields_named: &FieldsNamed, + rust_struct: &Ident, + capnp_struct: &Path, +) -> TokenStream { + let recurse = fields_named.named.iter().map(|field| gen_type_read(field)); + + quote! { + impl<'a> ReadCapnp<'a> for #rust_struct { + type ReaderType = #capnp_struct::Reader<'a>; + + fn read_capnp(reader: &Self::ReaderType) -> Result { + Ok(#rust_struct { + #(#recurse,)* + }) + } + } + } +} diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index ab628a113..2467d067b 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -14,318 +14,17 @@ extern crate proc_macro; -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; -use syn::spanned::Spanned; -// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, FieldsNamed, Ident, Path}; - -/// Is a primitive type? -fn is_primitive(path: &syn::Path) -> bool { - path.is_ident("u8") - || path.is_ident("u16") - || path.is_ident("u32") - || path.is_ident("u64") - || path.is_ident("i8") - || path.is_ident("i16") - || path.is_ident("i32") - || path.is_ident("i64") - || path.is_ident("bool") -} - -/// Check if the path represents a Vec -fn is_data(path: &syn::Path) -> bool { - let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - if &last_segment.ident.to_string() != "Vec" { - return false; - } - let angle = match &last_segment.arguments { - syn::PathArguments::AngleBracketed(angle) => { - if angle.args.len() > 1 { - unreachable!("Too many arguments for Vec!"); - } - angle - } - _ => unreachable!("Vec with arguments that are not angle bracketed!"), - }; - let last_arg = match angle.args.last().unwrap() { - syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return false, - }; - - let arg_ty = match last_arg { - syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return false, - }; - - let arg_ty_path = match arg_ty { - syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return false, - }; - - if arg_ty_path.qself.is_some() { - return false; - } - - if !arg_ty_path.path.is_ident("u8") { - return false; - } - - true -} - -/// Check if the path represents a Vec, where SomeStruct != u8 -fn get_list(path: &syn::Path) -> Option { - let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - if &last_segment.ident.to_string() != "Vec" { - return None; - } - let angle = match &last_segment.arguments { - syn::PathArguments::AngleBracketed(angle) => { - if angle.args.len() > 1 { - unreachable!("Too many arguments for Vec!"); - } - angle - } - _ => unreachable!("Vec with arguments that are not angle bracketed!"), - }; - let last_arg = match angle.args.last().unwrap() { - syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return None, - }; - - let arg_ty = match last_arg { - syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return None, - }; - - let arg_ty_path = match arg_ty { - syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return None, - }; - - if arg_ty_path.qself.is_some() { - return None; - } - - // Make sure that we don't deal with Vec: - if arg_ty_path.path.is_ident("u8") { - return None; - } - - Some(arg_ty_path.path.clone()) -} - -fn gen_list_write_iter(path: &syn::Path) -> TokenStream { - if is_primitive(path) || path.is_ident("String") || is_data(path) { - // A primitive list: - quote! { - list_builder - .reborrow() - .set(u32::try_from(index).unwrap(), item.clone()); - } - } else { - // Not a primitive list: - quote! { - let mut item_builder = list_builder - .reborrow() - .get(u32::try_from(index).unwrap()); - - item.write_capnp(&mut item_builder); - } - } - // TODO: It seems like we do not support List(List(...)) at the moment. - // How to support it? -} - -fn gen_type_write(field: &syn::Field) -> TokenStream { - match &field.ty { - syn::Type::Path(type_path) => { - if type_path.qself.is_some() { - // Self qualifier? - unimplemented!(); - } - - let path = &type_path.path; - - let name = &field.ident.as_ref().unwrap(); - - if is_primitive(path) { - let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); - return quote_spanned! {field.span() => - writer.reborrow().#set_method(self.#name); - }; - } - - if path.is_ident("String") || is_data(path) { - let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); - return quote_spanned! {field.span() => - writer.reborrow().#set_method(&self.#name); - }; - } - - if let Some(inner_path) = get_list(path) { - let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); - let list_write_iter = gen_list_write_iter(&inner_path); - - // In the cases of more complicated types, list_builder needs to be mutable. - let let_list_builder = - if is_primitive(path) || path.is_ident("String") || is_data(path) { - quote! { let list_builder } - } else { - quote! { let mut list_builder } - }; - - return quote_spanned! {field.span() => - #let_list_builder = writer - .reborrow() - .#init_method(u32::try_from(self.#name.len()).unwrap()); - - for (index, item) in self.#name.iter().enumerate() { - #list_write_iter - } - }; - } - - // Generic type: - let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); - quote_spanned! {field.span() => - self.#name.write_capnp(&mut writer.reborrow().#init_method()); - } - } - _ => unimplemented!(), - } -} - -fn gen_list_read_iter(path: &syn::Path) -> TokenStream { - if is_primitive(path) || path.is_ident("String") || is_data(path) { - // A primitive list: - quote! { - res_vec.push(item_reader.into()); - } - } else { - // Not a primitive list: - quote! { - res_vec.push(#path::read_capnp(&item_reader)?); - } - } - // TODO: It seems like we do not support List(List(...)) at the moment. - // How to support it? -} - -fn gen_type_read(field: &syn::Field) -> TokenStream { - match &field.ty { - syn::Type::Path(type_path) => { - if type_path.qself.is_some() { - // Self qualifier? - unimplemented!(); - } - - let path = &type_path.path; - - let name = &field.ident.as_ref().unwrap(); - - if is_primitive(path) { - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - return quote_spanned! {field.span() => - #name: reader.#get_method().into() - }; - } - - if path.is_ident("String") || is_data(path) { - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - return quote_spanned! {field.span() => - #name: reader.#get_method()?.into() - }; - } +mod derive_enum; +mod derive_struct; - if let Some(inner_path) = get_list(path) { - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - let list_read_iter = gen_list_read_iter(&inner_path); - return quote_spanned! {field.span() => - #name: { - let mut res_vec = Vec::new(); - for item_reader in reader.#get_method()? { - // res_vec.push_back(read_named_relay_address(&named_relay_address)?); - #list_read_iter - } - res_vec - } - }; - } - - // Generic type: - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - quote_spanned! {field.span() => - #name: #type_path::read_capnp(&reader.#get_method()?)? - } - } - _ => unimplemented!(), - } -} - -fn gen_write_capnp_named_struct( - fields_named: &FieldsNamed, - rust_struct: &Ident, - capnp_struct: &Path, -) -> TokenStream { - let recurse = fields_named - .named - .iter() - .map(|field| gen_type_write(&field)); - - quote! { - impl<'a> WriteCapnp<'a> for #rust_struct { - type WriterType = #capnp_struct::Builder<'a>; - - fn write_capnp(&self, writer: &mut Self::WriterType) { - #(#recurse)* - } - } - } -} - -fn gen_read_capnp_named_struct( - fields_named: &FieldsNamed, - rust_struct: &Ident, - capnp_struct: &Path, -) -> TokenStream { - let recurse = fields_named.named.iter().map(|field| gen_type_read(field)); - - quote! { - impl<'a> ReadCapnp<'a> for #rust_struct { - type ReaderType = #capnp_struct::Reader<'a>; - - fn read_capnp(reader: &Self::ReaderType) -> Result { - Ok(#rust_struct { - #(#recurse,)* - }) - } - } - } -} - -fn gen_write_capnp_enum( - _data_enum: &DataEnum, - _rust_struct: &Ident, - _capnp_struct: &Path, -) -> TokenStream { - unimplemented!(); -} +// use proc_macro2::TokenStream; +use quote::quote; +// use syn::spanned::Spanned; +// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use syn::{parse_macro_input, Data, DeriveInput, Fields, Path}; -fn gen_read_capnp_enum( - _data_enum: &DataEnum, - _rust_struct: &Ident, - _capnp_struct: &Path, -) -> TokenStream { - unimplemented!(); -} +use self::derive_enum::{gen_read_capnp_enum, gen_write_capnp_enum}; +use self::derive_struct::{gen_read_capnp_named_struct, gen_write_capnp_named_struct}; /// Generate code for conversion between Rust and capnp structs. #[proc_macro_attribute] From 43ca9135d6a7ec72c002e7bf4f2e65d333e6a7cc Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 18:43:38 +0300 Subject: [PATCH 20/50] capnp-conv-derive: Initial implementation of Enum support. --- capnp_conv_derive/Cargo.toml | 3 +- capnp_conv_derive/src/derive_enum.rs | 159 +++++++++++++++++++++++-- capnp_conv_derive/src/derive_struct.rs | 107 +---------------- capnp_conv_derive/src/lib.rs | 1 + capnp_conv_derive/src/util.rs | 103 ++++++++++++++++ src/lib.rs | 55 +++++++++ tests/capnp/test.capnp | 5 + tests/derive.rs | 15 ++- 8 files changed, 331 insertions(+), 117 deletions(-) create mode 100644 capnp_conv_derive/src/util.rs diff --git a/capnp_conv_derive/Cargo.toml b/capnp_conv_derive/Cargo.toml index 56463a5c5..c37d0d85d 100644 --- a/capnp_conv_derive/Cargo.toml +++ b/capnp_conv_derive/Cargo.toml @@ -13,6 +13,5 @@ proc-macro = true quote = "0.6.12" syn = "0.15.38" proc-macro2 = "0.4" - - +heck = "0.3.1" diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 90e240aba..1120957a9 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -1,20 +1,159 @@ use proc_macro2::TokenStream; -// use quote::{quote, quote_spanned}; +use quote::quote; +use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{DataEnum, Ident, Path}; +use syn::{DataEnum, Fields, Ident, Path, Variant}; +use heck::SnakeCase; + +use crate::util::{is_data, is_primitive}; + +fn gen_type_write(variant: &Variant) -> TokenStream { + // let variant_ident = &variant.ident; + let variant_name = &variant.ident; + let variant_snake_name = variant_name.to_string().to_snake_case(); + + match &variant.fields { + Fields::Unnamed(fields_unnamed) => { + let unnamed = &fields_unnamed.unnamed; + if unnamed.len() != 1 { + unimplemented!(); + } + + let pair = unnamed.last().unwrap(); + let last_ident = match pair { + syn::punctuated::Pair::End(last_ident) => last_ident, + _ => unreachable!(), + }; + + let path = match &last_ident.ty { + syn::Type::Path(type_path) => &type_path.path, + _ => unimplemented!(), + }; + + if is_primitive(path) || is_data(path) { + let set_method = + syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); + return quote! { + #variant_name(x) => writer.#set_method(x.clone()), + }; + } + + // TODO: Deal with the case of list here + + let init_method = + syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); + quote! { + #variant_name(x) => x.write_capnp(&mut writer.reborrow().#init_method()), + } + } + + Fields::Unit => { + let set_method = + syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); + quote! { + #variant_name => writer.#set_method(()), + } + } + // Rust enum variants don't have named fields (?) + Fields::Named(_) => unreachable!(), + } +} + +#[allow(unused)] pub fn gen_write_capnp_enum( - _data_enum: &DataEnum, - _rust_struct: &Ident, - _capnp_struct: &Path, + data_enum: &DataEnum, + rust_enum: &Ident, + capnp_struct: &Path, ) -> TokenStream { - unimplemented!(); + let recurse = data_enum.variants.iter().map(|variant| { + let type_write = gen_type_write(&variant); + quote! { + #rust_enum::#type_write + } + }); + + quote! { + impl<'a> WriteCapnp<'a> for #rust_enum { + type WriterType = #capnp_struct::Builder<'a>; + fn write_capnp(&self, writer: &mut Self::WriterType) { + match &self { + #(#recurse)* + }; + } + } + } +} + +fn gen_type_read(variant: &Variant, rust_enum: &Ident) -> TokenStream { + let variant_name = &variant.ident; + // let variant_snake_name = variant_name.to_string().to_snake_case(); + + match &variant.fields { + Fields::Unnamed(fields_unnamed) => { + let unnamed = &fields_unnamed.unnamed; + if unnamed.len() != 1 { + unimplemented!(); + } + + let pair = unnamed.last().unwrap(); + let last_ident = match pair { + syn::punctuated::Pair::End(last_ident) => last_ident, + _ => unreachable!(), + }; + + let path = match &last_ident.ty { + syn::Type::Path(type_path) => &type_path.path, + _ => unimplemented!(), + }; + + if is_primitive(path) || is_data(path) { + return quote! { + #variant_name(x) => #rust_enum::#variant_name(x), + }; + } + + // TODO: Deal with the case of list here + + quote! { + #variant_name(variant_reader) => { + // let variant_reader = variant_reader).into_result()?; + #rust_enum::#variant_name(#path::read_capnp(&variant_reader?)?) + }, + } + } + + Fields::Unit => { + quote! { + #variant_name(()) => #rust_enum::#variant_name, + } + } + // Rust enum variants don't have named fields (?) + Fields::Named(_) => unreachable!(), + } } pub fn gen_read_capnp_enum( - _data_enum: &DataEnum, - _rust_struct: &Ident, - _capnp_struct: &Path, + data_enum: &DataEnum, + rust_enum: &Ident, + capnp_struct: &Path, ) -> TokenStream { - unimplemented!(); + let recurse = data_enum.variants.iter().map(|variant| { + let type_read = gen_type_read(&variant, rust_enum); + quote! { + #capnp_struct::#type_read + } + }); + + quote! { + impl<'a> ReadCapnp<'a> for #rust_enum { + type ReaderType = #capnp_struct::Reader<'a>; + + fn read_capnp(reader: &Self::ReaderType) -> Result { + Ok(match reader.which()? { + #(#recurse)* + }) + } + } + } } diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index a6ed423f1..1c31cc36d 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -4,107 +4,7 @@ use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{FieldsNamed, Ident, Path}; -/// Is a primitive type? -fn is_primitive(path: &syn::Path) -> bool { - path.is_ident("u8") - || path.is_ident("u16") - || path.is_ident("u32") - || path.is_ident("u64") - || path.is_ident("i8") - || path.is_ident("i16") - || path.is_ident("i32") - || path.is_ident("i64") - || path.is_ident("bool") -} - -/// Check if the path represents a Vec -fn is_data(path: &syn::Path) -> bool { - let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - if &last_segment.ident.to_string() != "Vec" { - return false; - } - let angle = match &last_segment.arguments { - syn::PathArguments::AngleBracketed(angle) => { - if angle.args.len() > 1 { - unreachable!("Too many arguments for Vec!"); - } - angle - } - _ => unreachable!("Vec with arguments that are not angle bracketed!"), - }; - let last_arg = match angle.args.last().unwrap() { - syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return false, - }; - - let arg_ty = match last_arg { - syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return false, - }; - - let arg_ty_path = match arg_ty { - syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return false, - }; - - if arg_ty_path.qself.is_some() { - return false; - } - - if !arg_ty_path.path.is_ident("u8") { - return false; - } - - true -} - -/// Check if the path represents a Vec, where SomeStruct != u8 -fn get_list(path: &syn::Path) -> Option { - let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - if &last_segment.ident.to_string() != "Vec" { - return None; - } - let angle = match &last_segment.arguments { - syn::PathArguments::AngleBracketed(angle) => { - if angle.args.len() > 1 { - unreachable!("Too many arguments for Vec!"); - } - angle - } - _ => unreachable!("Vec with arguments that are not angle bracketed!"), - }; - let last_arg = match angle.args.last().unwrap() { - syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return None, - }; - - let arg_ty = match last_arg { - syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return None, - }; - - let arg_ty_path = match arg_ty { - syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return None, - }; - - if arg_ty_path.qself.is_some() { - return None; - } - - // Make sure that we don't deal with Vec: - if arg_ty_path.path.is_ident("u8") { - return None; - } - - Some(arg_ty_path.path.clone()) -} +use crate::util::{get_list, is_data, is_primitive}; fn gen_list_write_iter(path: &syn::Path) -> TokenStream { if is_primitive(path) || path.is_ident("String") || is_data(path) { @@ -247,7 +147,10 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { // Generic type: let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); quote_spanned! {field.span() => - #name: #type_path::read_capnp(&reader.#get_method()?)? + #name: { + let inner_reader = CapnpResult::from(reader.#get_method()).into_result()?; + #type_path::read_capnp(&inner_reader)? + } } } _ => unimplemented!(), diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 2467d067b..ca6ddef18 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -16,6 +16,7 @@ extern crate proc_macro; mod derive_enum; mod derive_struct; +mod util; // use proc_macro2::TokenStream; use quote::quote; diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs new file mode 100644 index 000000000..218eeb2f2 --- /dev/null +++ b/capnp_conv_derive/src/util.rs @@ -0,0 +1,103 @@ +// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; + +/// Is a primitive type? +pub fn is_primitive(path: &syn::Path) -> bool { + path.is_ident("u8") + || path.is_ident("u16") + || path.is_ident("u32") + || path.is_ident("u64") + || path.is_ident("i8") + || path.is_ident("i16") + || path.is_ident("i32") + || path.is_ident("i64") + || path.is_ident("bool") +} + +/// Check if the path represents a Vec +pub fn is_data(path: &syn::Path) -> bool { + let last_segment = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_segment) => last_segment, + _ => unreachable!(), + }; + if &last_segment.ident.to_string() != "Vec" { + return false; + } + let angle = match &last_segment.arguments { + syn::PathArguments::AngleBracketed(angle) => { + if angle.args.len() > 1 { + unreachable!("Too many arguments for Vec!"); + } + angle + } + _ => unreachable!("Vec with arguments that are not angle bracketed!"), + }; + let last_arg = match angle.args.last().unwrap() { + syn::punctuated::Pair::End(last_arg) => last_arg, + _ => return false, + }; + + let arg_ty = match last_arg { + syn::GenericArgument::Type(arg_ty) => arg_ty, + _ => return false, + }; + + let arg_ty_path = match arg_ty { + syn::Type::Path(arg_ty_path) => arg_ty_path, + _ => return false, + }; + + if arg_ty_path.qself.is_some() { + return false; + } + + if !arg_ty_path.path.is_ident("u8") { + return false; + } + + true +} + +/// Check if the path represents a Vec, where SomeStruct != u8 +pub fn get_list(path: &syn::Path) -> Option { + let last_segment = match path.segments.last().unwrap() { + syn::punctuated::Pair::End(last_segment) => last_segment, + _ => unreachable!(), + }; + if &last_segment.ident.to_string() != "Vec" { + return None; + } + let angle = match &last_segment.arguments { + syn::PathArguments::AngleBracketed(angle) => { + if angle.args.len() > 1 { + unreachable!("Too many arguments for Vec!"); + } + angle + } + _ => unreachable!("Vec with arguments that are not angle bracketed!"), + }; + let last_arg = match angle.args.last().unwrap() { + syn::punctuated::Pair::End(last_arg) => last_arg, + _ => return None, + }; + + let arg_ty = match last_arg { + syn::GenericArgument::Type(arg_ty) => arg_ty, + _ => return None, + }; + + let arg_ty_path = match arg_ty { + syn::Type::Path(arg_ty_path) => arg_ty_path, + _ => return None, + }; + + if arg_ty_path.qself.is_some() { + return None; + } + + // Make sure that we don't deal with Vec: + if arg_ty_path.path.is_ident("u8") { + return None; + } + + Some(arg_ty_path.path.clone()) +} diff --git a/src/lib.rs b/src/lib.rs index 6e08db3a4..1a02ceb82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ clippy::module_inception, clippy::new_without_default )] +#![feature(try_trait)] use std::io; @@ -56,6 +57,60 @@ pub trait FromCapnpBytes: Sized { fn from_capnp_bytes(bytes: &[u8]) -> Result; } +pub enum CapnpResult { + Ok(T), + Err(CapnpConvError), +} + +impl CapnpResult { + pub fn into_result(self) -> Result { + match self { + CapnpResult::Ok(t) => Ok(t), + CapnpResult::Err(e) => Err(e), + } + } +} + +impl From for CapnpResult { + fn from(input: T) -> Self { + CapnpResult::Ok(input) + } +} + +impl From> for CapnpResult +where + E: Into, +{ + fn from(input: Result) -> Self { + match input { + Ok(t) => CapnpResult::Ok(t), + Err(e) => CapnpResult::Err(e.into()), + } + } +} + +/* +impl std::ops::Try for T +where + T: for<'a> ReadCapnp<'a>, +{ + type Ok = T; + type Error = !; + + fn into_result(self) -> Result { + Ok(self) + } + + fn from_ok(v: Self::Ok) -> Self { + v + } + + fn from_error(_: Self::Error) -> ! { + unimplemented!(); + } +} +*/ + impl ToCapnpBytes for T where T: for<'a> WriteCapnp<'a>, diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index b5cfd95f4..d6f9b5471 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -21,4 +21,9 @@ struct TestStruct { structInner @11: TestStructInner; myPrimitiveList @12: List(UInt16); myList @13: List(TestStructInner); + inlineUnion: union { + firstVariant @14: UInt64; + secondVariant @15: TestStructInner; + thirdVariant @16: Void; + } } diff --git a/tests/derive.rs b/tests/derive.rs index 55fb8e258..6bcb5afde 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use offst_capnp_conv::{ - capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, + capnp_conv, CapnpConvError, CapnpResult, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, }; #[allow(unused)] @@ -11,13 +11,20 @@ mod test_capnp { include!(concat!(env!("OUT_DIR"), "/capnp/test_capnp.rs")); } -#[allow(unused)] #[capnp_conv(test_capnp::test_struct_inner)] #[derive(Debug, Clone, PartialEq)] struct TestStructInner { inner_u8: u8, } +#[capnp_conv(test_capnp::test_struct::inline_union)] +#[derive(Debug, Clone, PartialEq)] +enum InlineUnion { + FirstVariant(u64), + SecondVariant(TestStructInner), + ThirdVariant, +} + #[capnp_conv(test_capnp::test_struct)] #[derive(Debug, Clone, PartialEq)] struct TestStruct { @@ -37,10 +44,11 @@ struct TestStruct { struct_inner: TestStructInner, my_primitive_list: Vec, my_list: Vec, + inline_union: InlineUnion, } #[test] -fn capnp_serialize_basic_struct() { +fn capnp_serialize_basic_struct2() { // Serialize: let test_struct = TestStruct { my_bool: true, @@ -63,6 +71,7 @@ fn capnp_serialize_basic_struct() { TestStructInner { inner_u8: 3u8 }, TestStructInner { inner_u8: 4u8 }, ], + inline_union: InlineUnion::SecondVariant(TestStructInner { inner_u8: 5u8 }), }; let data = test_struct.to_capnp_bytes().unwrap(); From 280603d9da6cd5ff429c73497b29fc642a1feb5d Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 18:48:56 +0300 Subject: [PATCH 21/50] Added test for external capnp union. --- tests/capnp/test.capnp | 9 +++++++++ tests/derive.rs | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index d6f9b5471..97231ba0e 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -4,6 +4,14 @@ struct TestStructInner { innerU8 @0: UInt8; } +struct TestUnion { + union { + variantOne @0: UInt64; + variantTwo @1: TestStructInner; + variantThree @2: Void; + } +} + struct TestStruct { myBool @0: Bool; myInt8 @1: Int8; @@ -26,4 +34,5 @@ struct TestStruct { secondVariant @15: TestStructInner; thirdVariant @16: Void; } + externalUnion @17: TestUnion; } diff --git a/tests/derive.rs b/tests/derive.rs index 6bcb5afde..f1b22ad93 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -25,6 +25,14 @@ enum InlineUnion { ThirdVariant, } +#[capnp_conv(test_capnp::test_union)] +#[derive(Debug, Clone, PartialEq)] +enum TestUnion { + VariantOne(u64), + VariantTwo(TestStructInner), + VariantThree, +} + #[capnp_conv(test_capnp::test_struct)] #[derive(Debug, Clone, PartialEq)] struct TestStruct { @@ -45,6 +53,7 @@ struct TestStruct { my_primitive_list: Vec, my_list: Vec, inline_union: InlineUnion, + external_union: TestUnion, } #[test] @@ -72,6 +81,7 @@ fn capnp_serialize_basic_struct2() { TestStructInner { inner_u8: 4u8 }, ], inline_union: InlineUnion::SecondVariant(TestStructInner { inner_u8: 5u8 }), + external_union: TestUnion::VariantOne(6u64), }; let data = test_struct.to_capnp_bytes().unwrap(); From be1e69225c5a50094b9a82937ad7925c75e2c1bb Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 19:25:28 +0300 Subject: [PATCH 22/50] offst-capnp-conv: Added support for lists inside unions. --- capnp_conv_derive/src/derive_enum.rs | 44 ++++++++++++++++++++++++-- capnp_conv_derive/src/derive_struct.rs | 40 +---------------------- capnp_conv_derive/src/util.rs | 41 ++++++++++++++++++++++++ src/lib.rs | 25 +++------------ tests/capnp/test.capnp | 9 ++++++ tests/derive.rs | 13 ++++++++ 6 files changed, 109 insertions(+), 63 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 1120957a9..6fde2f852 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -6,7 +6,7 @@ use syn::{DataEnum, Fields, Ident, Path, Variant}; use heck::SnakeCase; -use crate::util::{is_data, is_primitive}; +use crate::util::{gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive}; fn gen_type_write(variant: &Variant) -> TokenStream { // let variant_ident = &variant.ident; @@ -39,7 +39,32 @@ fn gen_type_write(variant: &Variant) -> TokenStream { }; } - // TODO: Deal with the case of list here + // The case of list: + if let Some(inner_path) = get_list(path) { + let init_method = + syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); + let list_write_iter = gen_list_write_iter(&inner_path); + + // In the cases of more complicated types, list_builder needs to be mutable. + let let_list_builder = + if is_primitive(path) || path.is_ident("String") || is_data(path) { + quote! { let list_builder } + } else { + quote! { let mut list_builder } + }; + + return quote! { + #variant_name(vec) => { + #let_list_builder = writer + .reborrow() + .#init_method(u32::try_from(vec.len()).unwrap()); + + for (index, item) in vec.iter().enumerate() { + #list_write_iter + } + }, + }; + } let init_method = syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); @@ -113,7 +138,20 @@ fn gen_type_read(variant: &Variant, rust_enum: &Ident) -> TokenStream { }; } - // TODO: Deal with the case of list here + if let Some(inner_path) = get_list(path) { + // The case of a list: + let list_read_iter = gen_list_read_iter(&inner_path); + return quote! { + #variant_name(list_reader) => { + let mut res_vec = Vec::new(); + for item_reader in list_reader? { + // res_vec.push_back(read_named_relay_address(&named_relay_address)?); + #list_read_iter + } + #rust_enum::#variant_name(res_vec) + } + }; + } quote! { #variant_name(variant_reader) => { diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 1c31cc36d..271322c7b 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -4,29 +4,7 @@ use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{FieldsNamed, Ident, Path}; -use crate::util::{get_list, is_data, is_primitive}; - -fn gen_list_write_iter(path: &syn::Path) -> TokenStream { - if is_primitive(path) || path.is_ident("String") || is_data(path) { - // A primitive list: - quote! { - list_builder - .reborrow() - .set(u32::try_from(index).unwrap(), item.clone()); - } - } else { - // Not a primitive list: - quote! { - let mut item_builder = list_builder - .reborrow() - .get(u32::try_from(index).unwrap()); - - item.write_capnp(&mut item_builder); - } - } - // TODO: It seems like we do not support List(List(...)) at the moment. - // How to support it? -} +use crate::util::{gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive}; fn gen_type_write(field: &syn::Field) -> TokenStream { match &field.ty { @@ -87,22 +65,6 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { } } -fn gen_list_read_iter(path: &syn::Path) -> TokenStream { - if is_primitive(path) || path.is_ident("String") || is_data(path) { - // A primitive list: - quote! { - res_vec.push(item_reader.into()); - } - } else { - // Not a primitive list: - quote! { - res_vec.push(#path::read_capnp(&item_reader)?); - } - } - // TODO: It seems like we do not support List(List(...)) at the moment. - // How to support it? -} - fn gen_type_read(field: &syn::Field) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index 218eeb2f2..b62f0d7fc 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -1,5 +1,8 @@ // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use proc_macro2::TokenStream; +use quote::quote; + /// Is a primitive type? pub fn is_primitive(path: &syn::Path) -> bool { path.is_ident("u8") @@ -101,3 +104,41 @@ pub fn get_list(path: &syn::Path) -> Option { Some(arg_ty_path.path.clone()) } + +pub fn gen_list_write_iter(path: &syn::Path) -> TokenStream { + if is_primitive(path) || path.is_ident("String") || is_data(path) { + // A primitive list: + quote! { + list_builder + .reborrow() + .set(u32::try_from(index).unwrap(), item.clone()); + } + } else { + // Not a primitive list: + quote! { + let mut item_builder = list_builder + .reborrow() + .get(u32::try_from(index).unwrap()); + + item.write_capnp(&mut item_builder); + } + } + // TODO: It seems like we do not support List(List(...)) at the moment. + // How to support it? +} + +pub fn gen_list_read_iter(path: &syn::Path) -> TokenStream { + if is_primitive(path) || path.is_ident("String") || is_data(path) { + // A primitive list: + quote! { + res_vec.push(item_reader.into()); + } + } else { + // Not a primitive list: + quote! { + res_vec.push(#path::read_capnp(&item_reader)?); + } + } + // TODO: It seems like we do not support List(List(...)) at the moment. + // How to support it? +} diff --git a/src/lib.rs b/src/lib.rs index 1a02ceb82..43055f07e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,8 @@ pub trait FromCapnpBytes: Sized { fn from_capnp_bytes(bytes: &[u8]) -> Result; } +/// A shim allowing to merge cases where either +/// Result> or a T is returned. pub enum CapnpResult { Ok(T), Err(CapnpConvError), @@ -89,27 +91,8 @@ where } } -/* -impl std::ops::Try for T -where - T: for<'a> ReadCapnp<'a>, -{ - type Ok = T; - type Error = !; - - fn into_result(self) -> Result { - Ok(self) - } - - fn from_ok(v: Self::Ok) -> Self { - v - } - - fn from_error(_: Self::Error) -> ! { - unimplemented!(); - } -} -*/ +// ------------------------------------------------------- +// ------------------------------------------------------- impl ToCapnpBytes for T where diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 97231ba0e..960be95e0 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -12,6 +12,14 @@ struct TestUnion { } } +struct ListUnion { + union { + empty @0: Void; + withList @1: List(TestStructInner); + testUnion @2: TestUnion; + } +} + struct TestStruct { myBool @0: Bool; myInt8 @1: Int8; @@ -35,4 +43,5 @@ struct TestStruct { thirdVariant @16: Void; } externalUnion @17: TestUnion; + listUnion @18: ListUnion; } diff --git a/tests/derive.rs b/tests/derive.rs index f1b22ad93..dc000d314 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -33,6 +33,14 @@ enum TestUnion { VariantThree, } +#[capnp_conv(test_capnp::list_union)] +#[derive(Debug, Clone, PartialEq)] +enum ListUnion { + Empty, + WithList(Vec), + TestUnion(TestUnion), +} + #[capnp_conv(test_capnp::test_struct)] #[derive(Debug, Clone, PartialEq)] struct TestStruct { @@ -54,6 +62,7 @@ struct TestStruct { my_list: Vec, inline_union: InlineUnion, external_union: TestUnion, + list_union: ListUnion, } #[test] @@ -82,6 +91,10 @@ fn capnp_serialize_basic_struct2() { ], inline_union: InlineUnion::SecondVariant(TestStructInner { inner_u8: 5u8 }), external_union: TestUnion::VariantOne(6u64), + list_union: ListUnion::WithList(vec![ + TestStructInner { inner_u8: 10u8 }, + TestStructInner { inner_u8: 11u8 }, + ]), }; let data = test_struct.to_capnp_bytes().unwrap(); From d569a11084fb02d0cb3d1a853775f284328212ef Mon Sep 17 00:00:00 2001 From: real Date: Tue, 2 Jul 2019 19:37:13 +0300 Subject: [PATCH 23/50] Added support for floats in capnp conversion. --- capnp_conv_derive/src/lib.rs | 3 --- capnp_conv_derive/src/util.rs | 2 ++ tests/capnp/test.capnp | 5 +++++ tests/derive.rs | 38 +++++++++++++++++++++++++++++------ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index ca6ddef18..633212372 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -18,10 +18,7 @@ mod derive_enum; mod derive_struct; mod util; -// use proc_macro2::TokenStream; use quote::quote; -// use syn::spanned::Spanned; -// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{parse_macro_input, Data, DeriveInput, Fields, Path}; use self::derive_enum::{gen_read_capnp_enum, gen_write_capnp_enum}; diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index b62f0d7fc..a3e57a846 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -13,6 +13,8 @@ pub fn is_primitive(path: &syn::Path) -> bool { || path.is_ident("i16") || path.is_ident("i32") || path.is_ident("i64") + || path.is_ident("f32") + || path.is_ident("f64") || path.is_ident("bool") } diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 960be95e0..6b3130ed9 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -45,3 +45,8 @@ struct TestStruct { externalUnion @17: TestUnion; listUnion @18: ListUnion; } + +struct FloatStruct { + myFloat32 @0: Float32; + myFloat64 @1: Float64; +} diff --git a/tests/derive.rs b/tests/derive.rs index dc000d314..1a6916f52 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -53,8 +53,6 @@ struct TestStruct { my_uint16: u16, my_uint32: u32, my_uint64: u64, - // my_float32: f32, - // my_float64: f64, my_text: String, my_data: Vec, struct_inner: TestStructInner, @@ -66,8 +64,7 @@ struct TestStruct { } #[test] -fn capnp_serialize_basic_struct2() { - // Serialize: +fn capnp_serialize_basic_struct() { let test_struct = TestStruct { my_bool: true, my_int8: -1i8, @@ -78,8 +75,6 @@ fn capnp_serialize_basic_struct2() { my_uint16: 2u16, my_uint32: 3u32, my_uint64: 4u64, - // my_float32: -0.5f32, - // my_float64: 0.5f64, my_text: "my_text".to_owned(), my_data: vec![1, 2, 3, 4, 5u8], struct_inner: TestStructInner { inner_u8: 1u8 }, @@ -102,3 +97,34 @@ fn capnp_serialize_basic_struct2() { assert_eq!(test_struct, test_struct2); } + +#[capnp_conv(test_capnp::float_struct)] +#[derive(Debug, Clone)] +struct FloatStruct { + my_float32: f32, + my_float64: f64, +} + +/// We test floats separately, because in Rust floats to not implement PartialEq +#[test] +fn capnp_serialize_floats() { + let float_struct = FloatStruct { + my_float32: -0.5f32, + my_float64: 0.5f64, + }; + + let data = float_struct.to_capnp_bytes().unwrap(); + let float_struct2 = FloatStruct::from_capnp_bytes(&data).unwrap(); + + // Sloppily check that the floats are close enough (We can't compare them directly, as they + // don't implement PartialEq) + assert_eq!( + (float_struct.my_float32 * 10000.0).trunc(), + (float_struct2.my_float32 * 10000.0).trunc() + ); + + assert_eq!( + (float_struct.my_float64 * 10000.0).trunc(), + (float_struct2.my_float64 * 10000.0).trunc() + ); +} From 56df693fb46be20839c19d872b5f2d9bf5feb850 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 3 Jul 2019 16:08:41 +0300 Subject: [PATCH 24/50] Calling methods correctly inside macros. --- capnp_conv_derive/src/derive_enum.rs | 2 +- capnp_conv_derive/src/derive_struct.rs | 2 +- src/lib.rs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 6fde2f852..33b937871 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -156,7 +156,7 @@ fn gen_type_read(variant: &Variant, rust_enum: &Ident) -> TokenStream { quote! { #variant_name(variant_reader) => { // let variant_reader = variant_reader).into_result()?; - #rust_enum::#variant_name(#path::read_capnp(&variant_reader?)?) + #rust_enum::#variant_name(<#path>::read_capnp(&variant_reader?)?) }, } } diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 271322c7b..fae2d6f19 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -111,7 +111,7 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { quote_spanned! {field.span() => #name: { let inner_reader = CapnpResult::from(reader.#get_method()).into_result()?; - #type_path::read_capnp(&inner_reader)? + <#type_path>::read_capnp(&inner_reader)? } } } diff --git a/src/lib.rs b/src/lib.rs index 43055f07e..be36f179e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,9 @@ pub enum CapnpResult { Err(CapnpConvError), } +// ------------------------------------------------------- +// ------------------------------------------------------- + impl CapnpResult { pub fn into_result(self) -> Result { match self { From 8883047650bb943829e1fe54d6631c54f901395f Mon Sep 17 00:00:00 2001 From: real Date: Wed, 3 Jul 2019 16:15:57 +0300 Subject: [PATCH 25/50] Changed CapnpResult struct to be private. --- capnp_conv_derive/src/derive_struct.rs | 41 ++++++++++++++++++++++++++ capnp_conv_derive/src/lib.rs | 1 + src/lib.rs | 8 +++-- tests/derive.rs | 2 +- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index fae2d6f19..226f78480 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -65,6 +65,44 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { } } +/// A shim allowing to merge cases where either +/// Result> or a T is returned. +fn capnp_result_shim() -> TokenStream { + quote! { + pub enum CapnpResult { + Ok(T), + Err(CapnpConvError), + } + + impl CapnpResult { + pub fn into_result(self) -> Result { + match self { + CapnpResult::Ok(t) => Ok(t), + CapnpResult::Err(e) => Err(e), + } + } + } + + impl From for CapnpResult { + fn from(input: T) -> Self { + CapnpResult::Ok(input) + } + } + + impl From> for CapnpResult + where + E: Into, + { + fn from(input: Result) -> Self { + match input { + Ok(t) => CapnpResult::Ok(t), + Err(e) => CapnpResult::Err(e.into()), + } + } + } + } +} + fn gen_type_read(field: &syn::Field) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { @@ -108,8 +146,11 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { // Generic type: let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); + let capnp_result = capnp_result_shim(); quote_spanned! {field.span() => #name: { + #capnp_result + let inner_reader = CapnpResult::from(reader.#get_method()).into_result()?; <#type_path>::read_capnp(&inner_reader)? } diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 633212372..33c081aeb 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -2,6 +2,7 @@ #![feature(nll)] #![feature(generators)] #![feature(never_type)] +#![recursion_limit = "128"] #![deny(trivial_numeric_casts, warnings)] #![allow(intra_doc_link_resolution_failure)] #![allow( diff --git a/src/lib.rs b/src/lib.rs index be36f179e..b8dbe292b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,10 @@ pub trait FromCapnpBytes: Sized { fn from_capnp_bytes(bytes: &[u8]) -> Result; } +// ------------------------------------------------------- +// ------------------------------------------------------- + +/* /// A shim allowing to merge cases where either /// Result> or a T is returned. pub enum CapnpResult { @@ -64,9 +68,6 @@ pub enum CapnpResult { Err(CapnpConvError), } -// ------------------------------------------------------- -// ------------------------------------------------------- - impl CapnpResult { pub fn into_result(self) -> Result { match self { @@ -93,6 +94,7 @@ where } } } +*/ // ------------------------------------------------------- // ------------------------------------------------------- diff --git a/tests/derive.rs b/tests/derive.rs index 1a6916f52..738b8fafa 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use offst_capnp_conv::{ - capnp_conv, CapnpConvError, CapnpResult, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, + capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, }; #[allow(unused)] From b3f3cd142b62b0fe3d43f247c518fa70b630f338 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 3 Jul 2019 16:41:16 +0300 Subject: [PATCH 26/50] capnp-conv-derive: Removed dependency on std::convert::TryFrom --- capnp_conv_derive/src/derive_enum.rs | 8 ++++++-- capnp_conv_derive/src/derive_struct.rs | 22 ++++++++++++++++------ capnp_conv_derive/src/util.rs | 17 +++++++++++++++-- tests/derive.rs | 2 -- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 33b937871..959bc74bf 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -6,7 +6,9 @@ use syn::{DataEnum, Fields, Ident, Path, Variant}; use heck::SnakeCase; -use crate::util::{gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive}; +use crate::util::{ + gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, usize_to_u32_shim, +}; fn gen_type_write(variant: &Variant) -> TokenStream { // let variant_ident = &variant.ident; @@ -53,11 +55,13 @@ fn gen_type_write(variant: &Variant) -> TokenStream { quote! { let mut list_builder } }; + let usize_to_u32 = usize_to_u32_shim(); return quote! { #variant_name(vec) => { + #usize_to_u32 #let_list_builder = writer .reborrow() - .#init_method(u32::try_from(vec.len()).unwrap()); + .#init_method(usize_to_u32(vec.len()).unwrap()); for (index, item) in vec.iter().enumerate() { #list_write_iter diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 226f78480..88fd842f8 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -4,7 +4,9 @@ use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; use syn::{FieldsNamed, Ident, Path}; -use crate::util::{gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive}; +use crate::util::{ + gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, usize_to_u32_shim, +}; fn gen_type_write(field: &syn::Field) -> TokenStream { match &field.ty { @@ -44,13 +46,21 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { quote! { let mut list_builder } }; + let usize_to_u32 = usize_to_u32_shim(); + return quote_spanned! {field.span() => - #let_list_builder = writer - .reborrow() - .#init_method(u32::try_from(self.#name.len()).unwrap()); + { + #usize_to_u32 + + #let_list_builder = { + writer + .reborrow() + .#init_method(usize_to_u32(self.#name.len()).unwrap()) + }; - for (index, item) in self.#name.iter().enumerate() { - #list_write_iter + for (index, item) in self.#name.iter().enumerate() { + #list_write_iter + } } }; } diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index a3e57a846..c530e4751 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -3,6 +3,19 @@ use proc_macro2::TokenStream; use quote::quote; +/// A shim for converting usize to u32 +pub fn usize_to_u32_shim() -> TokenStream { + quote! { + pub fn usize_to_u32(num: usize) -> Option { + if num > 0xffffffff as usize { + None + } else { + Some(num as u32) + } + } + } +} + /// Is a primitive type? pub fn is_primitive(path: &syn::Path) -> bool { path.is_ident("u8") @@ -113,14 +126,14 @@ pub fn gen_list_write_iter(path: &syn::Path) -> TokenStream { quote! { list_builder .reborrow() - .set(u32::try_from(index).unwrap(), item.clone()); + .set(usize_to_u32(index).unwrap(), item.clone()); } } else { // Not a primitive list: quote! { let mut item_builder = list_builder .reborrow() - .get(u32::try_from(index).unwrap()); + .get(usize_to_u32(index).unwrap()); item.write_capnp(&mut item_builder); } diff --git a/tests/derive.rs b/tests/derive.rs index 738b8fafa..05958ba8f 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,7 +1,5 @@ #![deny(warnings)] -use std::convert::TryFrom; - use offst_capnp_conv::{ capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, }; From d84de5382738a7299a23c5489d0d6d2a969267eb Mon Sep 17 00:00:00 2001 From: real Date: Wed, 3 Jul 2019 16:55:56 +0300 Subject: [PATCH 27/50] Removed unused comment. --- src/lib.rs | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b8dbe292b..32c7f6bd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,48 +57,6 @@ pub trait FromCapnpBytes: Sized { fn from_capnp_bytes(bytes: &[u8]) -> Result; } -// ------------------------------------------------------- -// ------------------------------------------------------- - -/* -/// A shim allowing to merge cases where either -/// Result> or a T is returned. -pub enum CapnpResult { - Ok(T), - Err(CapnpConvError), -} - -impl CapnpResult { - pub fn into_result(self) -> Result { - match self { - CapnpResult::Ok(t) => Ok(t), - CapnpResult::Err(e) => Err(e), - } - } -} - -impl From for CapnpResult { - fn from(input: T) -> Self { - CapnpResult::Ok(input) - } -} - -impl From> for CapnpResult -where - E: Into, -{ - fn from(input: Result) -> Self { - match input { - Ok(t) => CapnpResult::Ok(t), - Err(e) => CapnpResult::Err(e.into()), - } - } -} -*/ - -// ------------------------------------------------------- -// ------------------------------------------------------- - impl ToCapnpBytes for T where T: for<'a> WriteCapnp<'a>, From af43e7c1d6910ff77c8629b3a8178cae5195f157 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 9 Jul 2019 12:45:49 +0300 Subject: [PATCH 28/50] offst-capnp-conv: Fixed case of Vec inside an Enum variant. --- capnp_conv_derive/src/derive_enum.rs | 16 +++++++++++++++- tests/capnp/test.capnp | 3 ++- tests/derive.rs | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 959bc74bf..fef6562a9 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -33,6 +33,14 @@ fn gen_type_write(variant: &Variant) -> TokenStream { _ => unimplemented!(), }; + if is_data(path) { + let set_method = + syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); + return quote! { + #variant_name(x) => writer.#set_method(x), + }; + } + if is_primitive(path) || is_data(path) { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); @@ -136,7 +144,13 @@ fn gen_type_read(variant: &Variant, rust_enum: &Ident) -> TokenStream { _ => unimplemented!(), }; - if is_primitive(path) || is_data(path) { + if is_data(path) { + return quote! { + #variant_name(x) => #rust_enum::#variant_name(x?.into()), + }; + } + + if is_primitive(path) { return quote! { #variant_name(x) => #rust_enum::#variant_name(x), }; diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 6b3130ed9..c4516c02b 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -16,7 +16,8 @@ struct ListUnion { union { empty @0: Void; withList @1: List(TestStructInner); - testUnion @2: TestUnion; + withData @2: Data; + testUnion @3: TestUnion; } } diff --git a/tests/derive.rs b/tests/derive.rs index 05958ba8f..9bfaa0084 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -36,6 +36,7 @@ enum TestUnion { enum ListUnion { Empty, WithList(Vec), + WithData(Vec), TestUnion(TestUnion), } From 129a5863f4bc07d6d3b0c8f56ba4adf66869d190 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 9 Jul 2019 18:34:40 +0300 Subject: [PATCH 29/50] Initial implementation of default args replacement. --- capnp_conv_derive/src/derive_struct.rs | 33 ++++++---- capnp_conv_derive/src/lib.rs | 21 ++++-- capnp_conv_derive/src/util.rs | 90 ++++++++++++++++++++++++++ tests/capnp/test.capnp | 6 ++ tests/derive.rs | 22 +++++++ 5 files changed, 155 insertions(+), 17 deletions(-) diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 88fd842f8..76061fc40 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -8,7 +8,7 @@ use crate::util::{ gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, usize_to_u32_shim, }; -fn gen_type_write(field: &syn::Field) -> TokenStream { +fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { if type_path.qself.is_some() { @@ -16,31 +16,32 @@ fn gen_type_write(field: &syn::Field) -> TokenStream { unimplemented!(); } - let path = &type_path.path; + let mut path = type_path.path.clone(); + assign_defaults(&mut path); let name = &field.ident.as_ref().unwrap(); - if is_primitive(path) { + if is_primitive(&path) { let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); return quote_spanned! {field.span() => writer.reborrow().#set_method(self.#name); }; } - if path.is_ident("String") || is_data(path) { + if path.is_ident("String") || is_data(&path) { let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); return quote_spanned! {field.span() => writer.reborrow().#set_method(&self.#name); }; } - if let Some(inner_path) = get_list(path) { + if let Some(inner_path) = get_list(&path) { let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); let list_write_iter = gen_list_write_iter(&inner_path); // In the cases of more complicated types, list_builder needs to be mutable. let let_list_builder = - if is_primitive(path) || path.is_ident("String") || is_data(path) { + if is_primitive(&path) || path.is_ident("String") || is_data(&path) { quote! { let list_builder } } else { quote! { let mut list_builder } @@ -113,7 +114,7 @@ fn capnp_result_shim() -> TokenStream { } } -fn gen_type_read(field: &syn::Field) -> TokenStream { +fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { if type_path.qself.is_some() { @@ -121,25 +122,26 @@ fn gen_type_read(field: &syn::Field) -> TokenStream { unimplemented!(); } - let path = &type_path.path; + let mut path = type_path.path.clone(); + assign_defaults(&mut path); let name = &field.ident.as_ref().unwrap(); - if is_primitive(path) { + if is_primitive(&path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); return quote_spanned! {field.span() => #name: reader.#get_method().into() }; } - if path.is_ident("String") || is_data(path) { + if path.is_ident("String") || is_data(&path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); return quote_spanned! {field.span() => #name: reader.#get_method()?.into() }; } - if let Some(inner_path) = get_list(path) { + if let Some(inner_path) = get_list(&path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); let list_read_iter = gen_list_read_iter(&inner_path); return quote_spanned! {field.span() => @@ -174,11 +176,12 @@ pub fn gen_write_capnp_named_struct( fields_named: &FieldsNamed, rust_struct: &Ident, capnp_struct: &Path, + assign_defaults: impl Fn(&mut syn::Path), ) -> TokenStream { let recurse = fields_named .named .iter() - .map(|field| gen_type_write(&field)); + .map(|field| gen_type_write(&field, &assign_defaults)); quote! { impl<'a> WriteCapnp<'a> for #rust_struct { @@ -195,8 +198,12 @@ pub fn gen_read_capnp_named_struct( fields_named: &FieldsNamed, rust_struct: &Ident, capnp_struct: &Path, + assign_defaults: impl Fn(&mut syn::Path), ) -> TokenStream { - let recurse = fields_named.named.iter().map(|field| gen_type_read(field)); + let recurse = fields_named + .named + .iter() + .map(|field| gen_type_read(field, &assign_defaults)); quote! { impl<'a> ReadCapnp<'a> for #rust_struct { diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 33c081aeb..61317a6cc 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -24,6 +24,7 @@ use syn::{parse_macro_input, Data, DeriveInput, Fields, Path}; use self::derive_enum::{gen_read_capnp_enum, gen_write_capnp_enum}; use self::derive_struct::{gen_read_capnp_named_struct, gen_write_capnp_named_struct}; +use self::util::{assign_defaults_path, extract_defaults}; /// Generate code for conversion between Rust and capnp structs. #[proc_macro_attribute] @@ -38,6 +39,9 @@ pub fn capnp_conv( let capnp_struct = parse_macro_input!(args as Path); let input = parse_macro_input!(input as DeriveInput); + let defaults = extract_defaults(&input.generics); + let assign_defaults = |path: &mut syn::Path| assign_defaults_path(path, &defaults); + // Name of local struct: let rust_struct = &input.ident; @@ -49,10 +53,19 @@ pub fn capnp_conv( // x: u32, // y: u32, // } - let write_capnp = - gen_write_capnp_named_struct(fields_named, rust_struct, &capnp_struct); - let read_capnp = - gen_read_capnp_named_struct(fields_named, rust_struct, &capnp_struct); + + let write_capnp = gen_write_capnp_named_struct( + fields_named, + rust_struct, + &capnp_struct, + &assign_defaults, + ); + let read_capnp = gen_read_capnp_named_struct( + fields_named, + rust_struct, + &capnp_struct, + &assign_defaults, + ); quote! { #write_capnp diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index c530e4751..954a5cf31 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -1,5 +1,7 @@ // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use std::collections::HashMap; + use proc_macro2::TokenStream; use quote::quote; @@ -157,3 +159,91 @@ pub fn gen_list_read_iter(path: &syn::Path) -> TokenStream { // TODO: It seems like we do not support List(List(...)) at the moment. // How to support it? } + +/// Obtain a map of default values from generics. +/// Example: +/// +/// ```text +/// struct MyStruct { ... } +/// ``` +/// +/// We expect to get a map, mapping A -> u32, B -> u64. +/// +pub fn extract_defaults(generics: &syn::Generics) -> HashMap { + let mut defaults = HashMap::new(); + for param in &generics.params { + let type_param = match *param { + syn::GenericParam::Type(ref type_param) => type_param, + _ => continue, + }; + + if type_param.eq_token.is_none() { + continue; + }; + + let default_type = match &type_param.default { + Some(default_type) => default_type, + None => continue, + }; + + let default_type_path = match default_type { + syn::Type::Path(default_type_path) => default_type_path, + _ => unimplemented!("Only paths default params are supported"), + }; + + if default_type_path.qself.is_some() { + unimplemented!("qself is not implemented!"); + } + + defaults.insert(type_param.ident.clone(), default_type_path.path.clone()); + } + defaults +} + +/// For every generic along a path, assign a default value if possible +pub fn assign_defaults_path(path: &mut syn::Path, defaults: &HashMap) { + // Deal with the case of a single Ident: `T` + if path.segments.len() == 1 { + let last_segment = match path.segments.last_mut().unwrap() { + syn::punctuated::Pair::End(last_segment) => last_segment, + _ => unreachable!(), + }; + + if let syn::PathArguments::None = last_segment.arguments { + if let Some(default_path) = defaults.get(&last_segment.ident) { + let _ = std::mem::replace(path, default_path.clone()); + return; + } + } + } + + // Deal with the more general case of a Path with various arguments + // that should be assigned their default value + + for segment in path.segments.iter_mut() { + let args = match &mut segment.arguments { + syn::PathArguments::None => continue, + syn::PathArguments::AngleBracketed(angle_bracketed) => &mut angle_bracketed.args, + _ => unimplemented!("Only angle bracketed arguments are supported!"), + }; + + for generic_arg in args.iter_mut() { + let ty = match generic_arg { + syn::GenericArgument::Type(ty) => ty, + _ => unimplemented!(), + }; + + let type_path = match ty { + syn::Type::Path(type_path) => type_path, + _ => unimplemented!(), + }; + + if type_path.qself.is_some() { + unimplemented!(); + } + + // Recursively replace default arguments: + assign_defaults_path(&mut type_path.path, defaults); + } + } +} diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index c4516c02b..fe25e50a2 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -51,3 +51,9 @@ struct FloatStruct { myFloat32 @0: Float32; myFloat64 @1: Float64; } + +struct GenericStruct { + a @0: UInt32; + b @1: UInt64; + c @2: UInt8; +} diff --git a/tests/derive.rs b/tests/derive.rs index 9bfaa0084..d2ff6dce4 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -127,3 +127,25 @@ fn capnp_serialize_floats() { (float_struct2.my_float64 * 10000.0).trunc() ); } + +#[capnp_conv(test_capnp::generic_struct)] +#[derive(Debug, Clone, PartialEq)] +struct GenericStruct { + a: A, + b: B, + c: u8, +} + +#[test] +fn capnp_serialize_generic_struct() { + let generic_struct = GenericStruct { + a: 1u32, + b: 2u64, + c: 3u8, + }; + + let data = generic_struct.to_capnp_bytes().unwrap(); + let generic_struct2 = GenericStruct::from_capnp_bytes(&data).unwrap(); + + assert_eq!(generic_struct, generic_struct2); +} From b524936fe25fb1f444b3787d680b3966414add43 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 9 Jul 2019 18:38:04 +0300 Subject: [PATCH 30/50] Extended capnp-conv generic derive test. --- tests/capnp/test.capnp | 2 ++ tests/derive.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index fe25e50a2..3db6a08c3 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -56,4 +56,6 @@ struct GenericStruct { a @0: UInt32; b @1: UInt64; c @2: UInt8; + d @3: Data; + e @4: List(TestStructInner); } diff --git a/tests/derive.rs b/tests/derive.rs index d2ff6dce4..cbb6e4953 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -130,10 +130,12 @@ fn capnp_serialize_floats() { #[capnp_conv(test_capnp::generic_struct)] #[derive(Debug, Clone, PartialEq)] -struct GenericStruct { +struct GenericStruct { a: A, b: B, c: u8, + d: Vec, + e: Vec, } #[test] @@ -142,6 +144,12 @@ fn capnp_serialize_generic_struct() { a: 1u32, b: 2u64, c: 3u8, + d: vec![1, 2, 3, 4u8], + e: vec![ + TestStructInner { inner_u8: 2u8 }, + TestStructInner { inner_u8: 3u8 }, + TestStructInner { inner_u8: 4u8 }, + ], }; let data = generic_struct.to_capnp_bytes().unwrap(); From b6533a47e03868dc850d29fcc4184e6a2a1232dd Mon Sep 17 00:00:00 2001 From: real Date: Tue, 9 Jul 2019 18:53:17 +0300 Subject: [PATCH 31/50] offst-capnp-conv: Added generic enums support. --- capnp_conv_derive/src/derive_enum.rs | 35 ++++++++++++++++++---------- capnp_conv_derive/src/lib.rs | 6 +++-- tests/capnp/test.capnp | 9 +++++++ tests/derive.rs | 23 ++++++++++++++++++ 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index fef6562a9..5a27d34a0 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -10,7 +10,7 @@ use crate::util::{ gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, usize_to_u32_shim, }; -fn gen_type_write(variant: &Variant) -> TokenStream { +fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { // let variant_ident = &variant.ident; let variant_name = &variant.ident; let variant_snake_name = variant_name.to_string().to_snake_case(); @@ -33,7 +33,10 @@ fn gen_type_write(variant: &Variant) -> TokenStream { _ => unimplemented!(), }; - if is_data(path) { + let mut path = path.clone(); + assign_defaults(&mut path); + + if is_data(&path) { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); return quote! { @@ -41,7 +44,7 @@ fn gen_type_write(variant: &Variant) -> TokenStream { }; } - if is_primitive(path) || is_data(path) { + if is_primitive(&path) || is_data(&path) { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); return quote! { @@ -50,14 +53,14 @@ fn gen_type_write(variant: &Variant) -> TokenStream { } // The case of list: - if let Some(inner_path) = get_list(path) { + if let Some(inner_path) = get_list(&path) { let init_method = syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); let list_write_iter = gen_list_write_iter(&inner_path); // In the cases of more complicated types, list_builder needs to be mutable. let let_list_builder = - if is_primitive(path) || path.is_ident("String") || is_data(path) { + if is_primitive(&path) || path.is_ident("String") || is_data(&path) { quote! { let list_builder } } else { quote! { let mut list_builder } @@ -97,14 +100,14 @@ fn gen_type_write(variant: &Variant) -> TokenStream { } } -#[allow(unused)] pub fn gen_write_capnp_enum( data_enum: &DataEnum, rust_enum: &Ident, capnp_struct: &Path, + assign_defaults: impl Fn(&mut syn::Path), ) -> TokenStream { let recurse = data_enum.variants.iter().map(|variant| { - let type_write = gen_type_write(&variant); + let type_write = gen_type_write(&variant, &assign_defaults); quote! { #rust_enum::#type_write } @@ -122,7 +125,11 @@ pub fn gen_write_capnp_enum( } } -fn gen_type_read(variant: &Variant, rust_enum: &Ident) -> TokenStream { +fn gen_type_read( + variant: &Variant, + rust_enum: &Ident, + assign_defaults: impl Fn(&mut syn::Path), +) -> TokenStream { let variant_name = &variant.ident; // let variant_snake_name = variant_name.to_string().to_snake_case(); @@ -144,19 +151,22 @@ fn gen_type_read(variant: &Variant, rust_enum: &Ident) -> TokenStream { _ => unimplemented!(), }; - if is_data(path) { + let mut path = path.clone(); + assign_defaults(&mut path); + + if is_data(&path) { return quote! { #variant_name(x) => #rust_enum::#variant_name(x?.into()), }; } - if is_primitive(path) { + if is_primitive(&path) { return quote! { #variant_name(x) => #rust_enum::#variant_name(x), }; } - if let Some(inner_path) = get_list(path) { + if let Some(inner_path) = get_list(&path) { // The case of a list: let list_read_iter = gen_list_read_iter(&inner_path); return quote! { @@ -193,9 +203,10 @@ pub fn gen_read_capnp_enum( data_enum: &DataEnum, rust_enum: &Ident, capnp_struct: &Path, + assign_defaults: impl Fn(&mut syn::Path), ) -> TokenStream { let recurse = data_enum.variants.iter().map(|variant| { - let type_read = gen_type_read(&variant, rust_enum); + let type_read = gen_type_read(&variant, rust_enum, &assign_defaults); quote! { #capnp_struct::#type_read } diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 61317a6cc..96a7f6b7d 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -81,8 +81,10 @@ pub fn capnp_conv( // Type2, // Type3(MyStruct), // } - let write_capnp = gen_write_capnp_enum(data_enum, rust_struct, &capnp_struct); - let read_capnp = gen_read_capnp_enum(data_enum, rust_struct, &capnp_struct); + let write_capnp = + gen_write_capnp_enum(data_enum, rust_struct, &capnp_struct, &assign_defaults); + let read_capnp = + gen_read_capnp_enum(data_enum, rust_struct, &capnp_struct, &assign_defaults); quote! { #write_capnp diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 3db6a08c3..2f9f929a8 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -59,3 +59,12 @@ struct GenericStruct { d @3: Data; e @4: List(TestStructInner); } + +struct GenericEnum { + union { + varA @0: UInt32; + varB @1: TestStructInner; + varC @2: UInt64; + varD @3: Data; + } +} diff --git a/tests/derive.rs b/tests/derive.rs index cbb6e4953..0bf98cb7b 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -157,3 +157,26 @@ fn capnp_serialize_generic_struct() { assert_eq!(generic_struct, generic_struct2); } + +#[capnp_conv(test_capnp::generic_enum)] +#[derive(Debug, Clone, PartialEq)] +enum GenericEnum> { + VarA(A), + VarB(B), + VarC(u64), + VarD(V), +} + +#[test] +fn capnp_serialize_generic_enum() { + for generic_enum in &[ + GenericEnum::VarA(1u32), + GenericEnum::VarB(TestStructInner { inner_u8: 2u8 }), + GenericEnum::VarC(3u64), + GenericEnum::VarD(vec![1, 2, 3, 4u8]), + ] { + let data = generic_enum.to_capnp_bytes().unwrap(); + let generic_enum2 = GenericEnum::from_capnp_bytes(&data).unwrap(); + assert_eq!(generic_enum.clone(), generic_enum2); + } +} From d2d838964900b6b1a9991875d01381c8faf3f19d Mon Sep 17 00:00:00 2001 From: real Date: Tue, 9 Jul 2019 19:36:31 +0300 Subject: [PATCH 32/50] offst-capnp-conv: Fixed bug in generic structs. --- capnp_conv_derive/src/derive_struct.rs | 2 +- capnp_conv_derive/src/util.rs | 1 + tests/capnp/test.capnp | 1 + tests/derive.rs | 6 ++++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 76061fc40..e39dc1b17 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -164,7 +164,7 @@ fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) - #capnp_result let inner_reader = CapnpResult::from(reader.#get_method()).into_result()?; - <#type_path>::read_capnp(&inner_reader)? + <#path>::read_capnp(&inner_reader)? } } } diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index 954a5cf31..bfb9258cc 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -203,6 +203,7 @@ pub fn extract_defaults(generics: &syn::Generics) -> HashMap) { // Deal with the case of a single Ident: `T` + if path.segments.len() == 1 { let last_segment = match path.segments.last_mut().unwrap() { syn::punctuated::Pair::End(last_segment) => last_segment, diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 2f9f929a8..a177986fc 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -58,6 +58,7 @@ struct GenericStruct { c @2: UInt8; d @3: Data; e @4: List(TestStructInner); + f @5: TestStructInner; } struct GenericEnum { diff --git a/tests/derive.rs b/tests/derive.rs index 0bf98cb7b..d68a4196f 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -132,10 +132,11 @@ fn capnp_serialize_floats() { #[derive(Debug, Clone, PartialEq)] struct GenericStruct { a: A, - b: B, + pub b: B, c: u8, - d: Vec, + pub d: Vec, e: Vec, + f: E, } #[test] @@ -150,6 +151,7 @@ fn capnp_serialize_generic_struct() { TestStructInner { inner_u8: 3u8 }, TestStructInner { inner_u8: 4u8 }, ], + f: TestStructInner { inner_u8: 5u8 }, }; let data = generic_struct.to_capnp_bytes().unwrap(); From d57bb388ff1b3f20b20c177ba96e322b28a77086 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 9 Jul 2019 20:11:44 +0300 Subject: [PATCH 33/50] Fixed proc macro bug in offst-capnp-conv. --- capnp_conv_derive/src/util.rs | 2 +- tests/capnp/test.capnp | 8 ++++++++ tests/derive.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index bfb9258cc..d6fb9eebe 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -153,7 +153,7 @@ pub fn gen_list_read_iter(path: &syn::Path) -> TokenStream { } else { // Not a primitive list: quote! { - res_vec.push(#path::read_capnp(&item_reader)?); + res_vec.push(<#path>::read_capnp(&item_reader)?); } } // TODO: It seems like we do not support List(List(...)) at the moment. diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index a177986fc..bffd2f6c7 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -69,3 +69,11 @@ struct GenericEnum { varD @3: Data; } } + +struct InnerGeneric { + a @0: UInt32; +} + +struct ListGeneric { + list @0: List(InnerGeneric); +} diff --git a/tests/derive.rs b/tests/derive.rs index d68a4196f..e71e2ef53 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -182,3 +182,31 @@ fn capnp_serialize_generic_enum() { assert_eq!(generic_enum.clone(), generic_enum2); } } + +#[capnp_conv(test_capnp::inner_generic)] +#[derive(Debug, Clone, PartialEq)] +struct InnerGeneric { + a: A, +} + +#[capnp_conv(test_capnp::list_generic)] +#[derive(Debug, Clone, PartialEq)] +struct ListGeneric { + list: Vec>, +} + +#[test] +fn capnp_serialize_generic_list() { + let list_generic = ListGeneric { + list: vec![ + InnerGeneric { a: 1u32 }, + InnerGeneric { a: 2u32 }, + InnerGeneric { a: 3u32 }, + ], + }; + + let data = list_generic.to_capnp_bytes().unwrap(); + let list_generic2 = ListGeneric::from_capnp_bytes(&data).unwrap(); + + assert_eq!(list_generic, list_generic2); +} From 24bee5031ef8fc61cc8dffd2cc1b625e2e9c4b97 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 10 Jul 2019 11:53:57 +0300 Subject: [PATCH 34/50] offst-capnp-conv: Fixed bug in case of inline nested enum. --- capnp_conv_derive/src/derive_enum.rs | 11 +++++-- capnp_conv_derive/src/derive_struct.rs | 41 ++------------------------ capnp_conv_derive/src/util.rs | 38 ++++++++++++++++++++++++ tests/capnp/test.capnp | 4 +++ tests/derive.rs | 8 +++++ 5 files changed, 60 insertions(+), 42 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 5a27d34a0..6268644d2 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -7,7 +7,8 @@ use syn::{DataEnum, Fields, Ident, Path, Variant}; use heck::SnakeCase; use crate::util::{ - gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, usize_to_u32_shim, + capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, + usize_to_u32_shim, }; fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { @@ -181,10 +182,14 @@ fn gen_type_read( }; } + let capnp_result = capnp_result_shim(); + quote! { #variant_name(variant_reader) => { - // let variant_reader = variant_reader).into_result()?; - #rust_enum::#variant_name(<#path>::read_capnp(&variant_reader?)?) + #capnp_result + + let variant_reader = CapnpResult::from(variant_reader).into_result()?; + #rust_enum::#variant_name(<#path>::read_capnp(&variant_reader)?) }, } } diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index e39dc1b17..3587094f3 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -5,7 +5,8 @@ use syn::spanned::Spanned; use syn::{FieldsNamed, Ident, Path}; use crate::util::{ - gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, usize_to_u32_shim, + capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, + usize_to_u32_shim, }; fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { @@ -76,44 +77,6 @@ fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) } } -/// A shim allowing to merge cases where either -/// Result> or a T is returned. -fn capnp_result_shim() -> TokenStream { - quote! { - pub enum CapnpResult { - Ok(T), - Err(CapnpConvError), - } - - impl CapnpResult { - pub fn into_result(self) -> Result { - match self { - CapnpResult::Ok(t) => Ok(t), - CapnpResult::Err(e) => Err(e), - } - } - } - - impl From for CapnpResult { - fn from(input: T) -> Self { - CapnpResult::Ok(input) - } - } - - impl From> for CapnpResult - where - E: Into, - { - fn from(input: Result) -> Self { - match input { - Ok(t) => CapnpResult::Ok(t), - Err(e) => CapnpResult::Err(e.into()), - } - } - } - } -} - fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { match &field.ty { syn::Type::Path(type_path) => { diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index d6fb9eebe..d7c5f694e 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -160,6 +160,44 @@ pub fn gen_list_read_iter(path: &syn::Path) -> TokenStream { // How to support it? } +/// A shim allowing to merge cases where either +/// Result> or a T is returned. +pub fn capnp_result_shim() -> TokenStream { + quote! { + pub enum CapnpResult { + Ok(T), + Err(CapnpConvError), + } + + impl CapnpResult { + pub fn into_result(self) -> Result { + match self { + CapnpResult::Ok(t) => Ok(t), + CapnpResult::Err(e) => Err(e), + } + } + } + + impl From for CapnpResult { + fn from(input: T) -> Self { + CapnpResult::Ok(input) + } + } + + impl From> for CapnpResult + where + E: Into, + { + fn from(input: Result) -> Self { + match input { + Ok(t) => CapnpResult::Ok(t), + Err(e) => CapnpResult::Err(e.into()), + } + } + } + } +} + /// Obtain a map of default values from generics. /// Example: /// diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index bffd2f6c7..927e79548 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -18,6 +18,10 @@ struct ListUnion { withList @1: List(TestStructInner); withData @2: Data; testUnion @3: TestUnion; + inlineInnerUnion: union { + ab @4: UInt32; + cd @5: UInt64; + } } } diff --git a/tests/derive.rs b/tests/derive.rs index e71e2ef53..32831b94d 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -31,6 +31,13 @@ enum TestUnion { VariantThree, } +#[capnp_conv(test_capnp::list_union::inline_inner_union)] +#[derive(Debug, Clone, PartialEq)] +enum InlineInnerUnion { + Ab(u32), + Cd(u64), +} + #[capnp_conv(test_capnp::list_union)] #[derive(Debug, Clone, PartialEq)] enum ListUnion { @@ -38,6 +45,7 @@ enum ListUnion { WithList(Vec), WithData(Vec), TestUnion(TestUnion), + InlineInnerUnion(InlineInnerUnion), } #[capnp_conv(test_capnp::test_struct)] From 0ccaf2fabbdc54e9eac56b4dd2a5d2614c652db4 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 10 Jul 2019 12:09:49 +0300 Subject: [PATCH 35/50] offst-capnp-conv: Fixed bug in case of a String inside an Enum. --- capnp_conv_derive/src/derive_enum.rs | 10 +++++++++- tests/capnp/test.capnp | 1 + tests/derive.rs | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 6268644d2..5105b66f7 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -45,6 +45,14 @@ fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) - }; } + if path.is_ident("String") { + let set_method = + syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); + return quote! { + #variant_name(x) => writer.#set_method(x), + }; + } + if is_primitive(&path) || is_data(&path) { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); @@ -155,7 +163,7 @@ fn gen_type_read( let mut path = path.clone(); assign_defaults(&mut path); - if is_data(&path) { + if is_data(&path) || path.is_ident("String") { return quote! { #variant_name(x) => #rust_enum::#variant_name(x?.into()), }; diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 927e79548..c05c02793 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -9,6 +9,7 @@ struct TestUnion { variantOne @0: UInt64; variantTwo @1: TestStructInner; variantThree @2: Void; + variantFour @3: Text; } } diff --git a/tests/derive.rs b/tests/derive.rs index 32831b94d..e6dc32723 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -29,6 +29,7 @@ enum TestUnion { VariantOne(u64), VariantTwo(TestStructInner), VariantThree, + VariantFour(String), } #[capnp_conv(test_capnp::list_union::inline_inner_union)] From 6d12a509d19751c121209ba44f881b77fdba6c1c Mon Sep 17 00:00:00 2001 From: real Date: Wed, 10 Jul 2019 13:32:45 +0300 Subject: [PATCH 36/50] capnp-conv + proto: Serialization should not fail. --- src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 32c7f6bd1..9dadd989a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,7 @@ pub trait ReadCapnp<'a>: Sized { pub trait ToCapnpBytes { /// Serialize a Rust struct into bytes using Capnp - fn to_capnp_bytes(&self) -> Result, CapnpConvError>; + fn to_capnp_bytes(&self) -> Vec; } pub trait FromCapnpBytes: Sized { @@ -61,7 +61,7 @@ impl ToCapnpBytes for T where T: for<'a> WriteCapnp<'a>, { - fn to_capnp_bytes(&self) -> Result, CapnpConvError> { + fn to_capnp_bytes(&self) -> Vec { let mut builder = capnp::message::Builder::new_default(); // A trick to avoid borrow checker issues: @@ -71,8 +71,9 @@ where } let mut data = Vec::new(); - capnp::serialize_packed::write_message(&mut data, &builder)?; - Ok(data) + // Should never really fail: + capnp::serialize_packed::write_message(&mut data, &builder).unwrap(); + data } } From e41a55fa5ceb044a3d6f902a77d688abde1f1b71 Mon Sep 17 00:00:00 2001 From: real Date: Thu, 11 Jul 2019 17:01:13 +0300 Subject: [PATCH 37/50] offst-capnp-conv: Some work on with attributes for structs. Not done yet. --- Cargo.toml | 5 ++ capnp_conv_derive/src/derive_struct.rs | 63 ++++++++++++++++--- capnp_conv_derive/src/lib.rs | 2 + capnp_conv_derive/src/util.rs | 4 +- tests/capnp/test.capnp | 17 +++++ tests/derive.rs | 10 +-- tests/with.rs | 86 ++++++++++++++++++++++++++ 7 files changed, 173 insertions(+), 14 deletions(-) create mode 100644 tests/with.rs diff --git a/Cargo.toml b/Cargo.toml index 99efb8bef..b516d11eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,8 @@ derive_more = "0.15.0" [build-dependencies] capnpc = "0.10.0" + +[dev-dependencies] + +byteorder = {version = "1.3.2", features = ["i128"]} + diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 3587094f3..6bd520089 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -1,8 +1,10 @@ +#![allow(unused)] + use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{FieldsNamed, Ident, Path}; +use syn::{parenthesized, parse_macro_input, FieldsNamed, Ident, Path, Token}; use crate::util::{ capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, @@ -10,6 +12,7 @@ use crate::util::{ }; fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { + let opt_with_path = get_with_attribute(field); match &field.ty { syn::Type::Path(type_path) => { if type_path.qself.is_some() { @@ -19,20 +22,21 @@ fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) let mut path = type_path.path.clone(); assign_defaults(&mut path); + let path = opt_with_path.unwrap_or(path); let name = &field.ident.as_ref().unwrap(); if is_primitive(&path) { let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); return quote_spanned! {field.span() => - writer.reborrow().#set_method(self.#name); + writer.reborrow().#set_method(<#path>::from(self.#name)); }; } if path.is_ident("String") || is_data(&path) { let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); return quote_spanned! {field.span() => - writer.reborrow().#set_method(&self.#name); + writer.reborrow().#set_method(&<#path>::from(self.#name.clone())); }; } @@ -70,14 +74,58 @@ fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) // Generic type: let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); quote_spanned! {field.span() => - self.#name.write_capnp(&mut writer.reborrow().#init_method()); + <#path>::from(self.#name.clone()).write_capnp(&mut writer.reborrow().#init_method()); } } _ => unimplemented!(), } } +#[derive(Debug)] +struct CapnpWithAttribute { + #[allow(dead_code)] + paren_token: syn::token::Paren, + with_ident: syn::Ident, + eq_token: Token![=], + path: syn::Path, +} + +impl syn::parse::Parse for CapnpWithAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + let content; + let paren_token = parenthesized!(content in input); + Ok(Self { + paren_token, + with_ident: content.parse()?, + eq_token: content.parse()?, + path: content.parse()?, + }) + } +} + +/// Get the path from a with style field attribute. +/// Example: +/// ```text +/// #[capnp_conv(with = Wrapper)] +/// ``` +/// Will return the path `Wrapper` +fn get_with_attribute(field: &syn::Field) -> Option { + let tts = proc_macro::TokenStream::from(TokenStream::new()); + // let capnp_with_attr = parse_macro_input!(tts as CapnpWithAttribute); + + for attr in &field.attrs { + if attr.path.is_ident("capnp_conv") { + let tts: proc_macro::TokenStream = attr.tts.clone().into(); + let capnp_with_attr = syn::parse::(tts).unwrap(); + return Some(capnp_with_attr.path); + } + } + None +} + fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { + let opt_with_path = get_with_attribute(field); + match &field.ty { syn::Type::Path(type_path) => { if type_path.qself.is_some() { @@ -88,6 +136,8 @@ fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) - let mut path = type_path.path.clone(); assign_defaults(&mut path); + let path = opt_with_path.unwrap_or(path); + let name = &field.ident.as_ref().unwrap(); if is_primitive(&path) { @@ -114,7 +164,7 @@ fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) - // res_vec.push_back(read_named_relay_address(&named_relay_address)?); #list_read_iter } - res_vec + res_vec.into() } }; } @@ -125,9 +175,8 @@ fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) - quote_spanned! {field.span() => #name: { #capnp_result - let inner_reader = CapnpResult::from(reader.#get_method()).into_result()?; - <#path>::read_capnp(&inner_reader)? + <#path>::read_capnp(&inner_reader)?.into() } } } diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 96a7f6b7d..d694c97e4 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -94,6 +94,8 @@ pub fn capnp_conv( Data::Union(_) => unimplemented!(), }; + // TODO: Remove all of our field attributes from the input. + let expanded = quote! { // Original structure #input diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index d7c5f694e..474c73ddb 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -128,7 +128,7 @@ pub fn gen_list_write_iter(path: &syn::Path) -> TokenStream { quote! { list_builder .reborrow() - .set(usize_to_u32(index).unwrap(), item.clone()); + .set(usize_to_u32(index).unwrap(), item.clone().into()); } } else { // Not a primitive list: @@ -137,7 +137,7 @@ pub fn gen_list_write_iter(path: &syn::Path) -> TokenStream { .reborrow() .get(usize_to_u32(index).unwrap()); - item.write_capnp(&mut item_builder); + item.clone().write_capnp(&mut item_builder); } } // TODO: It seems like we do not support List(List(...)) at the moment. diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index c05c02793..575e750b5 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -82,3 +82,20 @@ struct InnerGeneric { struct ListGeneric { list @0: List(InnerGeneric); } + +# A custom made 128 bit data structure. +struct Buffer128 { + x0 @0: UInt64; + x1 @1: UInt64; +} + +# Unsigned 128 bit integer +struct CustomUInt128 { + inner @0: Buffer128; +} + + +struct TestWithStruct { + a @0: CustomUInt128; + b @1: UInt64; +} diff --git a/tests/derive.rs b/tests/derive.rs index e6dc32723..278240307 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -100,7 +100,7 @@ fn capnp_serialize_basic_struct() { ]), }; - let data = test_struct.to_capnp_bytes().unwrap(); + let data = test_struct.to_capnp_bytes(); let test_struct2 = TestStruct::from_capnp_bytes(&data).unwrap(); assert_eq!(test_struct, test_struct2); @@ -121,7 +121,7 @@ fn capnp_serialize_floats() { my_float64: 0.5f64, }; - let data = float_struct.to_capnp_bytes().unwrap(); + let data = float_struct.to_capnp_bytes(); let float_struct2 = FloatStruct::from_capnp_bytes(&data).unwrap(); // Sloppily check that the floats are close enough (We can't compare them directly, as they @@ -163,7 +163,7 @@ fn capnp_serialize_generic_struct() { f: TestStructInner { inner_u8: 5u8 }, }; - let data = generic_struct.to_capnp_bytes().unwrap(); + let data = generic_struct.to_capnp_bytes(); let generic_struct2 = GenericStruct::from_capnp_bytes(&data).unwrap(); assert_eq!(generic_struct, generic_struct2); @@ -186,7 +186,7 @@ fn capnp_serialize_generic_enum() { GenericEnum::VarC(3u64), GenericEnum::VarD(vec![1, 2, 3, 4u8]), ] { - let data = generic_enum.to_capnp_bytes().unwrap(); + let data = generic_enum.to_capnp_bytes(); let generic_enum2 = GenericEnum::from_capnp_bytes(&data).unwrap(); assert_eq!(generic_enum.clone(), generic_enum2); } @@ -214,7 +214,7 @@ fn capnp_serialize_generic_list() { ], }; - let data = list_generic.to_capnp_bytes().unwrap(); + let data = list_generic.to_capnp_bytes(); let list_generic2 = ListGeneric::from_capnp_bytes(&data).unwrap(); assert_eq!(list_generic, list_generic2); diff --git a/tests/with.rs b/tests/with.rs new file mode 100644 index 000000000..1b9798c9e --- /dev/null +++ b/tests/with.rs @@ -0,0 +1,86 @@ +#![deny(warnings)] + +use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; +#[allow(unused)] +use offst_capnp_conv::{ + capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, +}; + +#[allow(unused)] +mod test_capnp { + include!(concat!(env!("OUT_DIR"), "/capnp/test_capnp.rs")); +} + +#[derive(derive_more::Constructor, Debug, Clone, Copy, PartialEq, Eq)] +pub struct Wrapper(T); + +/* +impl std::ops::Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +}*/ + +impl From for Wrapper { + fn from(t: T) -> Self { + Wrapper(t) + } +} + +impl Into for Wrapper { + fn into(self) -> u128 { + self.0 + } +} + +impl<'a> WriteCapnp<'a> for Wrapper { + type WriterType = crate::test_capnp::custom_u_int128::Builder<'a>; + + fn write_capnp(&self, writer: &mut Self::WriterType) { + let mut inner = writer.reborrow().get_inner().unwrap(); + + let mut data_bytes = Vec::new(); + + data_bytes + .write_u128::(self.clone().into()) + .unwrap(); + let mut cursor = std::io::Cursor::new(AsRef::<[u8]>::as_ref(&data_bytes)); + + inner.set_x0(cursor.read_u64::().unwrap()); + inner.set_x1(cursor.read_u64::().unwrap()); + } +} + +impl<'a> ReadCapnp<'a> for Wrapper { + type ReaderType = crate::test_capnp::custom_u_int128::Reader<'a>; + + fn read_capnp(reader: &Self::ReaderType) -> Result { + let inner = reader.get_inner()?; + let mut vec = Vec::new(); + vec.write_u64::(inner.get_x0())?; + vec.write_u64::(inner.get_x1())?; + Ok(Wrapper::new(BigEndian::read_u128(&vec[..]))) + } +} + +#[capnp_conv(test_capnp::test_with_struct)] +#[derive(Debug, Clone, PartialEq)] +struct TestWithStruct { + #[capnp_conv(with = Wrapper)] + a: u128, + b: u64, +} + +#[test] +fn capnp_serialize_with_struct() { + /* + let test_with_struct = TestWithStruct { a: 1u128, b: 2u64 }; + + let data = test_with_struct.to_capnp_bytes().unwrap(); + let test_with_struct2 = TestWithStruct::from_capnp_bytes(&data).unwrap(); + + assert_eq!(test_with_struct, test_with_struct2); + */ +} From 953ccaf2f26fb4378a3648b7f1ab1f2d0d7ce567 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 13 Jul 2019 14:05:08 +0300 Subject: [PATCH 38/50] offst-capnp-conv: Added support for 'with' attributes in named struct case --- capnp_conv_derive/src/derive_struct.rs | 40 ++++++++++++-------------- capnp_conv_derive/src/lib.rs | 6 ++-- capnp_conv_derive/src/util.rs | 26 +++++++++++++++++ tests/with.rs | 4 +-- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 6bd520089..03625d281 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -1,5 +1,3 @@ -#![allow(unused)] - use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; @@ -11,6 +9,24 @@ use crate::util::{ usize_to_u32_shim, }; +// TODO: Deal with the case of multiple with attributes (Should report error) +/// Get the path from a with style field attribute. +/// Example: +/// ```text +/// #[capnp_conv(with = Wrapper)] +/// ``` +/// Will return the path `Wrapper` +fn get_with_attribute(field: &syn::Field) -> Option { + for attr in &field.attrs { + if attr.path.is_ident("capnp_conv") { + let tts: proc_macro::TokenStream = attr.tts.clone().into(); + let capnp_with_attr = syn::parse::(tts).unwrap(); + return Some(capnp_with_attr.path); + } + } + None +} + fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { let opt_with_path = get_with_attribute(field); match &field.ty { @@ -103,26 +119,6 @@ impl syn::parse::Parse for CapnpWithAttribute { } } -/// Get the path from a with style field attribute. -/// Example: -/// ```text -/// #[capnp_conv(with = Wrapper)] -/// ``` -/// Will return the path `Wrapper` -fn get_with_attribute(field: &syn::Field) -> Option { - let tts = proc_macro::TokenStream::from(TokenStream::new()); - // let capnp_with_attr = parse_macro_input!(tts as CapnpWithAttribute); - - for attr in &field.attrs { - if attr.path.is_ident("capnp_conv") { - let tts: proc_macro::TokenStream = attr.tts.clone().into(); - let capnp_with_attr = syn::parse::(tts).unwrap(); - return Some(capnp_with_attr.path); - } - } - None -} - fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { let opt_with_path = get_with_attribute(field); diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index d694c97e4..4f2519bc8 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -24,7 +24,7 @@ use syn::{parse_macro_input, Data, DeriveInput, Fields, Path}; use self::derive_enum::{gen_read_capnp_enum, gen_write_capnp_enum}; use self::derive_struct::{gen_read_capnp_named_struct, gen_write_capnp_named_struct}; -use self::util::{assign_defaults_path, extract_defaults}; +use self::util::{assign_defaults_path, extract_defaults, remove_with_attributes}; /// Generate code for conversion between Rust and capnp structs. #[proc_macro_attribute] @@ -94,7 +94,9 @@ pub fn capnp_conv( Data::Union(_) => unimplemented!(), }; - // TODO: Remove all of our field attributes from the input. + // Remove all of our `#[capnp_conv(with = ... )]` attributes from the input: + let mut input = input; + remove_with_attributes(&mut input); let expanded = quote! { // Original structure diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index 474c73ddb..681442aea 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -1,4 +1,5 @@ // use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; +use syn; use std::collections::HashMap; @@ -286,3 +287,28 @@ pub fn assign_defaults_path(path: &mut syn::Path, defaults: &HashMap match data.fields { + syn::Fields::Named(ref mut fields_named) => { + for field in fields_named.named.iter_mut() { + // Remove all the attributes that look like: `capnp_conv(...)` + field.attrs.retain(|attr| !attr.path.is_ident("capnp_conv")); + } + } + syn::Fields::Unnamed(_) | syn::Fields::Unit => unimplemented!(), + }, + syn::Data::Enum(ref mut data_enum) => { + for variant in data_enum.variants.iter_mut() { + // Remove all the attributes that look like: `capnp_conv(...)` + variant + .attrs + .retain(|attr| !attr.path.is_ident("capnp_conv")); + } + } + + syn::Data::Union(_) => unimplemented!(), + }; +} diff --git a/tests/with.rs b/tests/with.rs index 1b9798c9e..b3be3652d 100644 --- a/tests/with.rs +++ b/tests/with.rs @@ -75,12 +75,10 @@ struct TestWithStruct { #[test] fn capnp_serialize_with_struct() { - /* let test_with_struct = TestWithStruct { a: 1u128, b: 2u64 }; - let data = test_with_struct.to_capnp_bytes().unwrap(); + let data = test_with_struct.to_capnp_bytes(); let test_with_struct2 = TestWithStruct::from_capnp_bytes(&data).unwrap(); assert_eq!(test_with_struct, test_with_struct2); - */ } From a6ba42b5c43606293cb41aa0ad89acfd8bcca984 Mon Sep 17 00:00:00 2001 From: real Date: Sat, 13 Jul 2019 14:56:31 +0300 Subject: [PATCH 39/50] offst-capnp-conv: Added 'with' attributes support to Enums. --- capnp_conv_derive/src/derive_enum.rs | 48 +++++++++++++++++++------- capnp_conv_derive/src/derive_struct.rs | 28 ++------------- capnp_conv_derive/src/util.rs | 22 ++++++++++++ tests/capnp/test.capnp | 8 +++++ tests/with.rs | 19 ++++++++++ 5 files changed, 87 insertions(+), 38 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 5105b66f7..b6580c7a0 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -8,10 +8,29 @@ use heck::SnakeCase; use crate::util::{ capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, - usize_to_u32_shim, + usize_to_u32_shim, CapnpWithAttribute, }; +// TODO: Deal with the case of multiple with attributes (Should report error) +/// Get the path from a with style field attribute. +/// Example: +/// ```text +/// #[capnp_conv(with = Wrapper)] +/// ``` +/// Will return the path `Wrapper` +fn get_with_attribute(variant: &syn::Variant) -> Option { + for attr in &variant.attrs { + if attr.path.is_ident("capnp_conv") { + let tts: proc_macro::TokenStream = attr.tts.clone().into(); + let capnp_with_attr = syn::parse::(tts).unwrap(); + return Some(capnp_with_attr.path); + } + } + None +} + fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { + let opt_with_path = get_with_attribute(variant); // let variant_ident = &variant.ident; let variant_name = &variant.ident; let variant_snake_name = variant_name.to_string().to_snake_case(); @@ -36,28 +55,29 @@ fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) - let mut path = path.clone(); assign_defaults(&mut path); + let path = opt_with_path.unwrap_or(path); - if is_data(&path) { + if is_primitive(&path) { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); return quote! { - #variant_name(x) => writer.#set_method(x), + #variant_name(x) => writer.#set_method(<#path>::from(x.clone())), }; } - if path.is_ident("String") { + if is_data(&path) { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); return quote! { - #variant_name(x) => writer.#set_method(x), + #variant_name(x) => writer.#set_method(&<#path>::from(x.clone())), }; } - if is_primitive(&path) || is_data(&path) { + if path.is_ident("String") { let set_method = syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); return quote! { - #variant_name(x) => writer.#set_method(x.clone()), + #variant_name(x) => writer.#set_method(x), }; } @@ -93,7 +113,7 @@ fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) - let init_method = syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); quote! { - #variant_name(x) => x.write_capnp(&mut writer.reborrow().#init_method()), + #variant_name(x) => <#path>::from(x.clone()).write_capnp(&mut writer.reborrow().#init_method()), } } @@ -139,6 +159,7 @@ fn gen_type_read( rust_enum: &Ident, assign_defaults: impl Fn(&mut syn::Path), ) -> TokenStream { + let opt_with_path = get_with_attribute(variant); let variant_name = &variant.ident; // let variant_snake_name = variant_name.to_string().to_snake_case(); @@ -162,16 +183,17 @@ fn gen_type_read( let mut path = path.clone(); assign_defaults(&mut path); + let path = opt_with_path.unwrap_or(path); - if is_data(&path) || path.is_ident("String") { + if is_primitive(&path) { return quote! { - #variant_name(x) => #rust_enum::#variant_name(x?.into()), + #variant_name(x) => #rust_enum::#variant_name(x.into()), }; } - if is_primitive(&path) { + if is_data(&path) || path.is_ident("String") { return quote! { - #variant_name(x) => #rust_enum::#variant_name(x), + #variant_name(x) => #rust_enum::#variant_name(x?.into()), }; } @@ -197,7 +219,7 @@ fn gen_type_read( #capnp_result let variant_reader = CapnpResult::from(variant_reader).into_result()?; - #rust_enum::#variant_name(<#path>::read_capnp(&variant_reader)?) + #rust_enum::#variant_name(<#path>::read_capnp(&variant_reader)?.into()) }, } } diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 03625d281..0153e473e 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -1,12 +1,12 @@ use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; + use syn::spanned::Spanned; -// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{parenthesized, parse_macro_input, FieldsNamed, Ident, Path, Token}; +use syn::{FieldsNamed, Ident, Path}; use crate::util::{ capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, - usize_to_u32_shim, + usize_to_u32_shim, CapnpWithAttribute, }; // TODO: Deal with the case of multiple with attributes (Should report error) @@ -97,28 +97,6 @@ fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) } } -#[derive(Debug)] -struct CapnpWithAttribute { - #[allow(dead_code)] - paren_token: syn::token::Paren, - with_ident: syn::Ident, - eq_token: Token![=], - path: syn::Path, -} - -impl syn::parse::Parse for CapnpWithAttribute { - fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { - let content; - let paren_token = parenthesized!(content in input); - Ok(Self { - paren_token, - with_ident: content.parse()?, - eq_token: content.parse()?, - path: content.parse()?, - }) - } -} - fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { let opt_with_path = get_with_attribute(field); diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index 681442aea..5fb86321c 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -312,3 +312,25 @@ pub fn remove_with_attributes(input: &mut syn::DeriveInput) { syn::Data::Union(_) => unimplemented!(), }; } + +#[derive(Debug)] +pub struct CapnpWithAttribute { + #[allow(dead_code)] + pub paren_token: syn::token::Paren, + pub with_ident: syn::Ident, + pub eq_token: syn::Token![=], + pub path: syn::Path, +} + +impl syn::parse::Parse for CapnpWithAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + let content; + let paren_token = syn::parenthesized!(content in input); + Ok(Self { + paren_token, + with_ident: content.parse()?, + eq_token: content.parse()?, + path: content.parse()?, + }) + } +} diff --git a/tests/capnp/test.capnp b/tests/capnp/test.capnp index 575e750b5..61ca67a6b 100644 --- a/tests/capnp/test.capnp +++ b/tests/capnp/test.capnp @@ -99,3 +99,11 @@ struct TestWithStruct { a @0: CustomUInt128; b @1: UInt64; } + +struct TestWithEnum { + union { + varA @0: CustomUInt128; + varB @1: UInt64; + varC @2: Void; + } +} diff --git a/tests/with.rs b/tests/with.rs index b3be3652d..e1795da7d 100644 --- a/tests/with.rs +++ b/tests/with.rs @@ -82,3 +82,22 @@ fn capnp_serialize_with_struct() { assert_eq!(test_with_struct, test_with_struct2); } + +#[capnp_conv(test_capnp::test_with_enum)] +#[derive(Debug, Clone, PartialEq)] +enum TestWithEnum { + #[capnp_conv(with = Wrapper)] + VarA(u128), + VarB(u64), + VarC, +} + +#[test] +fn capnp_serialize_with_enum() { + let test_with_enum = TestWithEnum::VarA(1u128); + + let data = test_with_enum.to_capnp_bytes(); + let test_with_enum2 = TestWithEnum::from_capnp_bytes(&data).unwrap(); + + assert_eq!(test_with_enum, test_with_enum2); +} From d56d721455eb9d83616765978571a46ab882b29b Mon Sep 17 00:00:00 2001 From: real Date: Sat, 13 Jul 2019 15:23:15 +0300 Subject: [PATCH 40/50] Renamed get_list -> get_vec. --- capnp_conv_derive/src/derive_enum.rs | 6 +++--- capnp_conv_derive/src/derive_struct.rs | 6 +++--- capnp_conv_derive/src/util.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index b6580c7a0..2a9986135 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -7,7 +7,7 @@ use syn::{DataEnum, Fields, Ident, Path, Variant}; use heck::SnakeCase; use crate::util::{ - capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, + capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_vec, is_data, is_primitive, usize_to_u32_shim, CapnpWithAttribute, }; @@ -82,7 +82,7 @@ fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) - } // The case of list: - if let Some(inner_path) = get_list(&path) { + if let Some(inner_path) = get_vec(&path) { let init_method = syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); let list_write_iter = gen_list_write_iter(&inner_path); @@ -197,7 +197,7 @@ fn gen_type_read( }; } - if let Some(inner_path) = get_list(&path) { + if let Some(inner_path) = get_vec(&path) { // The case of a list: let list_read_iter = gen_list_read_iter(&inner_path); return quote! { diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 0153e473e..3ce17a7a4 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -5,7 +5,7 @@ use syn::spanned::Spanned; use syn::{FieldsNamed, Ident, Path}; use crate::util::{ - capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_list, is_data, is_primitive, + capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_vec, is_data, is_primitive, usize_to_u32_shim, CapnpWithAttribute, }; @@ -56,7 +56,7 @@ fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) }; } - if let Some(inner_path) = get_list(&path) { + if let Some(inner_path) = get_vec(&path) { let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); let list_write_iter = gen_list_write_iter(&inner_path); @@ -128,7 +128,7 @@ fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) - }; } - if let Some(inner_path) = get_list(&path) { + if let Some(inner_path) = get_vec(&path) { let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); let list_read_iter = gen_list_read_iter(&inner_path); return quote_spanned! {field.span() => diff --git a/capnp_conv_derive/src/util.rs b/capnp_conv_derive/src/util.rs index 5fb86321c..0c8b0f600 100644 --- a/capnp_conv_derive/src/util.rs +++ b/capnp_conv_derive/src/util.rs @@ -79,7 +79,7 @@ pub fn is_data(path: &syn::Path) -> bool { } /// Check if the path represents a Vec, where SomeStruct != u8 -pub fn get_list(path: &syn::Path) -> Option { +pub fn get_vec(path: &syn::Path) -> Option { let last_segment = match path.segments.last().unwrap() { syn::punctuated::Pair::End(last_segment) => last_segment, _ => unreachable!(), From ee94ab5c82591037eca4deb8eac2f422eefd298e Mon Sep 17 00:00:00 2001 From: real Date: Sat, 13 Jul 2019 16:57:08 +0300 Subject: [PATCH 41/50] Fixed bug using 'with' attribute over a Variant(Tuple). --- capnp_conv_derive/src/derive_enum.rs | 41 ++++++++++++++++++-------- capnp_conv_derive/src/derive_struct.rs | 4 +-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index 2a9986135..d6c8d1c6a 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -39,7 +39,7 @@ fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) - Fields::Unnamed(fields_unnamed) => { let unnamed = &fields_unnamed.unnamed; if unnamed.len() != 1 { - unimplemented!(); + unimplemented!("gen_type_write: Amount of unnamed fields is not 1!"); } let pair = unnamed.last().unwrap(); @@ -48,14 +48,27 @@ fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) - _ => unreachable!(), }; - let path = match &last_ident.ty { - syn::Type::Path(type_path) => &type_path.path, - _ => unimplemented!(), + let path = match opt_with_path { + Some(with_path) => with_path, + None => match &last_ident.ty { + syn::Type::Path(type_path) => type_path.path.clone(), + _ => { + panic!("{:?}", opt_with_path); + } + }, }; - - let mut path = path.clone(); + /* + let path = opt_with_path.clone().unwrap_or(match &last_ident.ty { + syn::Type::Path(type_path) => type_path.path.clone(), + _ => { + panic!("{:?}", opt_with_path); + // unimplemented!("gen_type_write: last ident is not a path!"), + } + }); + */ + + let mut path = path; assign_defaults(&mut path); - let path = opt_with_path.unwrap_or(path); if is_primitive(&path) { let set_method = @@ -167,7 +180,7 @@ fn gen_type_read( Fields::Unnamed(fields_unnamed) => { let unnamed = &fields_unnamed.unnamed; if unnamed.len() != 1 { - unimplemented!(); + unimplemented!("gen_type_read: Amount of unnamed fields is not 1!"); } let pair = unnamed.last().unwrap(); @@ -176,14 +189,18 @@ fn gen_type_read( _ => unreachable!(), }; - let path = match &last_ident.ty { - syn::Type::Path(type_path) => &type_path.path, - _ => unimplemented!(), + let path = match opt_with_path { + Some(with_path) => with_path, + None => match &last_ident.ty { + syn::Type::Path(type_path) => type_path.path.clone(), + _ => { + panic!("{:?}", opt_with_path); + } + }, }; let mut path = path.clone(); assign_defaults(&mut path); - let path = opt_with_path.unwrap_or(path); if is_primitive(&path) { return quote! { diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp_conv_derive/src/derive_struct.rs index 3ce17a7a4..bde1bc0c3 100644 --- a/capnp_conv_derive/src/derive_struct.rs +++ b/capnp_conv_derive/src/derive_struct.rs @@ -33,7 +33,7 @@ fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) syn::Type::Path(type_path) => { if type_path.qself.is_some() { // Self qualifier? - unimplemented!(); + unimplemented!("self qualifier"); } let mut path = type_path.path.clone(); @@ -104,7 +104,7 @@ fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) - syn::Type::Path(type_path) => { if type_path.qself.is_some() { // Self qualifier? - unimplemented!(); + unimplemented!("self qualifier"); } let mut path = type_path.path.clone(); From 9bf3cbe077074c44c74c00201522c6d530a413a6 Mon Sep 17 00:00:00 2001 From: real Date: Tue, 16 Jul 2019 12:28:56 +0300 Subject: [PATCH 42/50] offst-capnp-conv: Added hints to ignore clippy warnings. --- capnp_conv_derive/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 4f2519bc8..2efdc1e48 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -68,7 +68,9 @@ pub fn capnp_conv( ); quote! { + #[allow(clippy::all)] #write_capnp + #[allow(clippy::all)] #read_capnp } } @@ -87,7 +89,9 @@ pub fn capnp_conv( gen_read_capnp_enum(data_enum, rust_struct, &capnp_struct, &assign_defaults); quote! { + #[allow(clippy::all)] #write_capnp + #[allow(clippy::all)] #read_capnp } } From 53c427fe3d179b97c64fbc139336263328b41430 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 18 Sep 2019 17:05:02 +0300 Subject: [PATCH 43/50] capnp_conv: Removed old async_await and await_macro features. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9dadd989a..b0aa32608 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![crate_type = "lib"] -#![feature(async_await, await_macro, arbitrary_self_types)] +#![feature(arbitrary_self_types)] #![feature(nll)] #![feature(generators)] #![feature(never_type)] From b80e691e953f0044b78b5b024e8713ad7ea3b803 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 13 Nov 2019 17:47:29 +0200 Subject: [PATCH 44/50] Began porting to stable toolchain. offst-common compiles. --- capnp_conv_derive/src/lib.rs | 3 --- src/lib.rs | 5 ----- 2 files changed, 8 deletions(-) diff --git a/capnp_conv_derive/src/lib.rs b/capnp_conv_derive/src/lib.rs index 2efdc1e48..713fdfc28 100644 --- a/capnp_conv_derive/src/lib.rs +++ b/capnp_conv_derive/src/lib.rs @@ -1,7 +1,4 @@ #![crate_type = "lib"] -#![feature(nll)] -#![feature(generators)] -#![feature(never_type)] #![recursion_limit = "128"] #![deny(trivial_numeric_casts, warnings)] #![allow(intra_doc_link_resolution_failure)] diff --git a/src/lib.rs b/src/lib.rs index b0aa32608..185554e54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,4 @@ #![crate_type = "lib"] -#![feature(arbitrary_self_types)] -#![feature(nll)] -#![feature(generators)] -#![feature(never_type)] #![deny(trivial_numeric_casts, warnings)] #![allow(intra_doc_link_resolution_failure)] #![allow( @@ -11,7 +7,6 @@ clippy::module_inception, clippy::new_without_default )] -#![feature(try_trait)] use std::io; From aae23a8cb5c283cfdbbba44d94f2f62cbe431f01 Mon Sep 17 00:00:00 2001 From: real Date: Thu, 19 Dec 2019 20:53:02 +0200 Subject: [PATCH 45/50] Some clippy fixes. Not done yet. --- capnp_conv_derive/src/derive_enum.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp_conv_derive/src/derive_enum.rs index d6c8d1c6a..bd4962054 100644 --- a/capnp_conv_derive/src/derive_enum.rs +++ b/capnp_conv_derive/src/derive_enum.rs @@ -189,7 +189,7 @@ fn gen_type_read( _ => unreachable!(), }; - let path = match opt_with_path { + let mut path = match opt_with_path { Some(with_path) => with_path, None => match &last_ident.ty { syn::Type::Path(type_path) => type_path.path.clone(), @@ -199,7 +199,6 @@ fn gen_type_read( }, }; - let mut path = path.clone(); assign_defaults(&mut path); if is_primitive(&path) { From 68a31058fcb68813edb2901b1ba220c5050ec74f Mon Sep 17 00:00:00 2001 From: real Date: Wed, 15 Jan 2020 19:26:00 +0200 Subject: [PATCH 46/50] Collected into capnp-conv --- Cargo.toml => capnp-conv/Cargo.toml | 0 LICENSE-APACHE => capnp-conv/LICENSE-APACHE | 0 LICENSE-MIT => capnp-conv/LICENSE-MIT | 0 build.rs => capnp-conv/build.rs | 0 {capnp_conv_derive => capnp-conv/capnp_conv_derive}/Cargo.toml | 0 .../capnp_conv_derive}/LICENSE-APACHE | 0 {capnp_conv_derive => capnp-conv/capnp_conv_derive}/LICENSE-MIT | 0 .../capnp_conv_derive}/src/derive_enum.rs | 0 .../capnp_conv_derive}/src/derive_struct.rs | 0 {capnp_conv_derive => capnp-conv/capnp_conv_derive}/src/lib.rs | 0 {capnp_conv_derive => capnp-conv/capnp_conv_derive}/src/util.rs | 0 {src => capnp-conv/src}/lib.rs | 0 {tests => capnp-conv/tests}/capnp/test.capnp | 0 {tests => capnp-conv/tests}/derive.rs | 0 {tests => capnp-conv/tests}/with.rs | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename Cargo.toml => capnp-conv/Cargo.toml (100%) rename LICENSE-APACHE => capnp-conv/LICENSE-APACHE (100%) rename LICENSE-MIT => capnp-conv/LICENSE-MIT (100%) rename build.rs => capnp-conv/build.rs (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/Cargo.toml (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/LICENSE-APACHE (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/LICENSE-MIT (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/src/derive_enum.rs (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/src/derive_struct.rs (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/src/lib.rs (100%) rename {capnp_conv_derive => capnp-conv/capnp_conv_derive}/src/util.rs (100%) rename {src => capnp-conv/src}/lib.rs (100%) rename {tests => capnp-conv/tests}/capnp/test.capnp (100%) rename {tests => capnp-conv/tests}/derive.rs (100%) rename {tests => capnp-conv/tests}/with.rs (100%) diff --git a/Cargo.toml b/capnp-conv/Cargo.toml similarity index 100% rename from Cargo.toml rename to capnp-conv/Cargo.toml diff --git a/LICENSE-APACHE b/capnp-conv/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to capnp-conv/LICENSE-APACHE diff --git a/LICENSE-MIT b/capnp-conv/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to capnp-conv/LICENSE-MIT diff --git a/build.rs b/capnp-conv/build.rs similarity index 100% rename from build.rs rename to capnp-conv/build.rs diff --git a/capnp_conv_derive/Cargo.toml b/capnp-conv/capnp_conv_derive/Cargo.toml similarity index 100% rename from capnp_conv_derive/Cargo.toml rename to capnp-conv/capnp_conv_derive/Cargo.toml diff --git a/capnp_conv_derive/LICENSE-APACHE b/capnp-conv/capnp_conv_derive/LICENSE-APACHE similarity index 100% rename from capnp_conv_derive/LICENSE-APACHE rename to capnp-conv/capnp_conv_derive/LICENSE-APACHE diff --git a/capnp_conv_derive/LICENSE-MIT b/capnp-conv/capnp_conv_derive/LICENSE-MIT similarity index 100% rename from capnp_conv_derive/LICENSE-MIT rename to capnp-conv/capnp_conv_derive/LICENSE-MIT diff --git a/capnp_conv_derive/src/derive_enum.rs b/capnp-conv/capnp_conv_derive/src/derive_enum.rs similarity index 100% rename from capnp_conv_derive/src/derive_enum.rs rename to capnp-conv/capnp_conv_derive/src/derive_enum.rs diff --git a/capnp_conv_derive/src/derive_struct.rs b/capnp-conv/capnp_conv_derive/src/derive_struct.rs similarity index 100% rename from capnp_conv_derive/src/derive_struct.rs rename to capnp-conv/capnp_conv_derive/src/derive_struct.rs diff --git a/capnp_conv_derive/src/lib.rs b/capnp-conv/capnp_conv_derive/src/lib.rs similarity index 100% rename from capnp_conv_derive/src/lib.rs rename to capnp-conv/capnp_conv_derive/src/lib.rs diff --git a/capnp_conv_derive/src/util.rs b/capnp-conv/capnp_conv_derive/src/util.rs similarity index 100% rename from capnp_conv_derive/src/util.rs rename to capnp-conv/capnp_conv_derive/src/util.rs diff --git a/src/lib.rs b/capnp-conv/src/lib.rs similarity index 100% rename from src/lib.rs rename to capnp-conv/src/lib.rs diff --git a/tests/capnp/test.capnp b/capnp-conv/tests/capnp/test.capnp similarity index 100% rename from tests/capnp/test.capnp rename to capnp-conv/tests/capnp/test.capnp diff --git a/tests/derive.rs b/capnp-conv/tests/derive.rs similarity index 100% rename from tests/derive.rs rename to capnp-conv/tests/derive.rs diff --git a/tests/with.rs b/capnp-conv/tests/with.rs similarity index 100% rename from tests/with.rs rename to capnp-conv/tests/with.rs From 2385db182dfa0fa398092a5dfa2865d2b7ed1ab7 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 15 Jan 2020 19:35:17 +0200 Subject: [PATCH 47/50] Updated .gitignore to ignore any target directories --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 49bdc4424..6724e2745 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,5 @@ # Generated by Cargo Cargo.lock -/target/ +target/ From 886779a4488f499cd95ac1a5b91dbc4e1304cc32 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 15 Jan 2020 19:35:44 +0200 Subject: [PATCH 48/50] capnp-conv: Some fixes after migration from offst --- Cargo.toml | 4 +- capnp-conv/Cargo.toml | 8 +- capnp-conv/LICENSE-APACHE | 201 ------------------ capnp-conv/LICENSE-MIT | 25 --- .../Cargo.toml | 4 +- .../src/derive_enum.rs | 0 .../src/derive_struct.rs | 0 .../src/lib.rs | 0 .../src/util.rs | 0 capnp-conv/capnp_conv_derive/LICENSE-APACHE | 201 ------------------ capnp-conv/capnp_conv_derive/LICENSE-MIT | 25 --- capnp-conv/tests/derive.rs | 6 +- capnp-conv/tests/with.rs | 16 +- 13 files changed, 11 insertions(+), 479 deletions(-) delete mode 100644 capnp-conv/LICENSE-APACHE delete mode 100644 capnp-conv/LICENSE-MIT rename capnp-conv/{capnp_conv_derive => capnp-conv-derive}/Cargo.toml (75%) rename capnp-conv/{capnp_conv_derive => capnp-conv-derive}/src/derive_enum.rs (100%) rename capnp-conv/{capnp_conv_derive => capnp-conv-derive}/src/derive_struct.rs (100%) rename capnp-conv/{capnp_conv_derive => capnp-conv-derive}/src/lib.rs (100%) rename capnp-conv/{capnp_conv_derive => capnp-conv-derive}/src/util.rs (100%) delete mode 100644 capnp-conv/capnp_conv_derive/LICENSE-APACHE delete mode 100644 capnp-conv/capnp_conv_derive/LICENSE-MIT diff --git a/Cargo.toml b/Cargo.toml index 3cb6ae949..5911073c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "capnpc", "capnp-futures", "capnp-rpc", + "capnp-conv", # testing and examples "benchmark", @@ -20,4 +21,5 @@ members = [ default-members = [ "capnp", "capnpc", -] \ No newline at end of file + "capnp-conv", +] diff --git a/capnp-conv/Cargo.toml b/capnp-conv/Cargo.toml index b516d11eb..34e1c0538 100644 --- a/capnp-conv/Cargo.toml +++ b/capnp-conv/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "offst-capnp-conv" -version = "0.1.0" +name = "capnp-conv" +version = "0.11.0" authors = ["real "] -license = "MIT OR Apache-2.0" +license = "MIT" edition = "2018" [dependencies] @@ -10,7 +10,7 @@ edition = "2018" log = "0.4" pretty_env_logger = "0.2" -capnp_conv_derive = { path = "capnp_conv_derive", version = "0.1.0", package = "offst-capnp-conv-derive" } +capnp_conv_derive = { path = "capnp-conv-derive", version = "0.1.0", package = "capnp-conv-derive" } capnp = "0.10.0" derive_more = "0.15.0" diff --git a/capnp-conv/LICENSE-APACHE b/capnp-conv/LICENSE-APACHE deleted file mode 100644 index c753170bc..000000000 --- a/capnp-conv/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright (c) 2019 real - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/capnp-conv/LICENSE-MIT b/capnp-conv/LICENSE-MIT deleted file mode 100644 index 683ce3d46..000000000 --- a/capnp-conv/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019 real - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/capnp-conv/capnp_conv_derive/Cargo.toml b/capnp-conv/capnp-conv-derive/Cargo.toml similarity index 75% rename from capnp-conv/capnp_conv_derive/Cargo.toml rename to capnp-conv/capnp-conv-derive/Cargo.toml index c37d0d85d..9d822e946 100644 --- a/capnp-conv/capnp_conv_derive/Cargo.toml +++ b/capnp-conv/capnp-conv-derive/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "offst-capnp-conv-derive" +name = "capnp-conv-derive" version = "0.1.0" authors = ["real "] -license = "MIT OR Apache-2.0" +license = "MIT" edition = "2018" [lib] diff --git a/capnp-conv/capnp_conv_derive/src/derive_enum.rs b/capnp-conv/capnp-conv-derive/src/derive_enum.rs similarity index 100% rename from capnp-conv/capnp_conv_derive/src/derive_enum.rs rename to capnp-conv/capnp-conv-derive/src/derive_enum.rs diff --git a/capnp-conv/capnp_conv_derive/src/derive_struct.rs b/capnp-conv/capnp-conv-derive/src/derive_struct.rs similarity index 100% rename from capnp-conv/capnp_conv_derive/src/derive_struct.rs rename to capnp-conv/capnp-conv-derive/src/derive_struct.rs diff --git a/capnp-conv/capnp_conv_derive/src/lib.rs b/capnp-conv/capnp-conv-derive/src/lib.rs similarity index 100% rename from capnp-conv/capnp_conv_derive/src/lib.rs rename to capnp-conv/capnp-conv-derive/src/lib.rs diff --git a/capnp-conv/capnp_conv_derive/src/util.rs b/capnp-conv/capnp-conv-derive/src/util.rs similarity index 100% rename from capnp-conv/capnp_conv_derive/src/util.rs rename to capnp-conv/capnp-conv-derive/src/util.rs diff --git a/capnp-conv/capnp_conv_derive/LICENSE-APACHE b/capnp-conv/capnp_conv_derive/LICENSE-APACHE deleted file mode 100644 index c753170bc..000000000 --- a/capnp-conv/capnp_conv_derive/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright (c) 2019 real - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/capnp-conv/capnp_conv_derive/LICENSE-MIT b/capnp-conv/capnp_conv_derive/LICENSE-MIT deleted file mode 100644 index 683ce3d46..000000000 --- a/capnp-conv/capnp_conv_derive/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019 real - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/capnp-conv/tests/derive.rs b/capnp-conv/tests/derive.rs index 278240307..4f5e27187 100644 --- a/capnp-conv/tests/derive.rs +++ b/capnp-conv/tests/derive.rs @@ -1,8 +1,4 @@ -#![deny(warnings)] - -use offst_capnp_conv::{ - capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, -}; +use capnp_conv::{capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp}; #[allow(unused)] mod test_capnp { diff --git a/capnp-conv/tests/with.rs b/capnp-conv/tests/with.rs index e1795da7d..6dcf177f7 100644 --- a/capnp-conv/tests/with.rs +++ b/capnp-conv/tests/with.rs @@ -1,10 +1,5 @@ -#![deny(warnings)] - use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; -#[allow(unused)] -use offst_capnp_conv::{ - capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, -}; +use capnp_conv::{capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp}; #[allow(unused)] mod test_capnp { @@ -14,15 +9,6 @@ mod test_capnp { #[derive(derive_more::Constructor, Debug, Clone, Copy, PartialEq, Eq)] pub struct Wrapper(T); -/* -impl std::ops::Deref for Wrapper { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -}*/ - impl From for Wrapper { fn from(t: T) -> Self { Wrapper(t) From 6783a0408b2610a70f778950edb1a601f3d6c467 Mon Sep 17 00:00:00 2001 From: real Date: Wed, 15 Jan 2020 19:47:04 +0200 Subject: [PATCH 49/50] capnp-conv: Make dependencies point to local repository --- capnp-conv/Cargo.toml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/capnp-conv/Cargo.toml b/capnp-conv/Cargo.toml index 34e1c0538..26d09df98 100644 --- a/capnp-conv/Cargo.toml +++ b/capnp-conv/Cargo.toml @@ -7,18 +7,14 @@ edition = "2018" [dependencies] -log = "0.4" -pretty_env_logger = "0.2" - capnp_conv_derive = { path = "capnp-conv-derive", version = "0.1.0", package = "capnp-conv-derive" } -capnp = "0.10.0" +capnp = { path = "../capnp", package = "capnp" } derive_more = "0.15.0" [build-dependencies] -capnpc = "0.10.0" +capnpc = { path = "../capnpc", package = "capnpc" } [dev-dependencies] byteorder = {version = "1.3.2", features = ["i128"]} - From 63618957b1b061512b1f84ac8ccb10c0531a74bf Mon Sep 17 00:00:00 2001 From: real Date: Wed, 15 Jan 2020 19:47:13 +0200 Subject: [PATCH 50/50] capnp-conv: Update authors --- capnp-conv/Cargo.toml | 2 +- capnp-conv/capnp-conv-derive/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/capnp-conv/Cargo.toml b/capnp-conv/Cargo.toml index 26d09df98..1a2c870a4 100644 --- a/capnp-conv/Cargo.toml +++ b/capnp-conv/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "capnp-conv" version = "0.11.0" -authors = ["real "] +authors = [ "David Renshaw ", "real "] license = "MIT" edition = "2018" diff --git a/capnp-conv/capnp-conv-derive/Cargo.toml b/capnp-conv/capnp-conv-derive/Cargo.toml index 9d822e946..7641df45c 100644 --- a/capnp-conv/capnp-conv-derive/Cargo.toml +++ b/capnp-conv/capnp-conv-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "capnp-conv-derive" version = "0.1.0" -authors = ["real "] +authors = [ "David Renshaw ", "real "] license = "MIT" edition = "2018"