Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ resolver = "2"
members = ["capnp_conv", "capnp_conv_macros", "capnp_conv_tests", "example"]

[workspace.package]
authors = ["Aik Kalantarian <aik.kalantarian@gmail.com>"]
version = "0.3.1"
edition = "2021"
authors = [
"Aik Kalantarian <aik.kalantarian@gmail.com>",
"Debanjan Basu <debanjanbasu2006@gmail.com>",
]
version = "0.3.2"
edition = "2024"
license = "MIT"
repository = "https://github.com/aikalant/capnp_conv"
readme = "README.md"
Expand All @@ -14,8 +17,8 @@ readme = "README.md"
capnp_conv = { path = "capnp_conv", version = "0.3.1" }
capnp_conv_macros = { path = "capnp_conv_macros", version = "0.3.1" }

capnp = "0.20"
capnpc = "0.20"
capnp = "0.21"
capnpc = "0.21"
heck = "0.5"
proc-macro2 = "1.0"
quote = "1.0"
Expand Down
13 changes: 12 additions & 1 deletion capnp_conv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
use capnp::{traits::Owned, Result};
#![deny(
clippy::nursery,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::string_slice,
clippy::pedantic
)]
#![forbid(unsafe_code)]

use capnp::{Result, traits::Owned};
pub use capnp_conv_macros::capnp_conv;

pub trait Writable {
Expand All @@ -13,6 +23,7 @@ where
{
type OwnedType: Owned;

#[allow(clippy::missing_errors_doc)]
fn read(reader: <Self::OwnedType as Owned>::Reader<'_>) -> Result<Self>;
}

Expand Down
124 changes: 57 additions & 67 deletions capnp_conv_macros/src/generators.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use heck::{ToSnakeCase, ToUpperCamelCase};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, ToTokens};
use quote::{ToTokens, format_ident, quote};
use syn::{Ident, Path};

use crate::{
Expand All @@ -11,17 +11,17 @@ use crate::{
impl ItemInfo {
pub fn generate_impls(&self, capnp_path: &Path) -> TokenStream2 {
let impls = match self {
ItemInfo::Struct(struct_info) => vec![
Self::Struct(struct_info) => vec![
struct_info.generate_writer_impl(capnp_path),
struct_info.generate_reader_impl(capnp_path),
struct_info.generate_try_from_impl(capnp_path),
],
ItemInfo::Enum(enum_info) if enum_info.is_union() => vec![
Self::Enum(enum_info) if enum_info.is_union() => vec![
enum_info.generate_writer_impl(capnp_path),
enum_info.generate_reader_impl(capnp_path),
enum_info.generate_try_from_impl(capnp_path),
],
ItemInfo::Enum(enum_info) => vec![
Self::Enum(enum_info) => vec![
enum_info.generate_into_impl(capnp_path),
enum_info.generate_from_impl(capnp_path),
enum_info.generate_to_impl(capnp_path),
Expand Down Expand Up @@ -238,10 +238,10 @@ impl FieldInfo {
if matches!(self.field_type, FieldType::Phantom) {
quote!(::std::marker::PhantomData)
} else if self.skip_read {
let field_reader = match &self.default_override {
Some(default_override) => quote!(#default_override()),
None => self.generate_default_reader(),
};
let field_reader = self.default_override.as_ref().map_or_else(
|| self.generate_default_reader(),
|default_override| quote!(#default_override()),
);
if self.is_optional {
quote!(Some(#field_reader))
} else {
Expand Down Expand Up @@ -279,16 +279,16 @@ impl FieldInfo {
fn generate_default_reader(&self) -> TokenStream2 {
let path = match &self.field_type {
FieldType::Void() => return quote!(()),
FieldType::Primitive(path) => path,
FieldType::Data(path) => path,
FieldType::Text(path) => path,
FieldType::Struct(path) => path,
FieldType::EnumRemote(path) => path,
FieldType::Enum(path) => path,
FieldType::GroupOrUnion(path) => path,
FieldType::UnnamedUnion(path) => path,
FieldType::Primitive(path)
| FieldType::Data(path)
| FieldType::Text(path)
| FieldType::Struct(path)
| FieldType::EnumRemote(path)
| FieldType::Enum(path)
| FieldType::GroupOrUnion(path)
| FieldType::UnnamedUnion(path)
| FieldType::GenericStruct(path) => path,
FieldType::List(_) => return quote!(Vec::default()),
FieldType::GenericStruct(path) => path,
_ => unimplemented!(),
};
let path = as_turbofish(path);
Expand Down Expand Up @@ -366,27 +366,27 @@ impl FieldType {
quote!(#reader_name.#getter())
};
match self {
FieldType::Phantom => unimplemented!(),
FieldType::EnumVariant => unimplemented!(),
FieldType::Void() => quote!(()),
FieldType::Primitive(_) => quote!(#getter),
FieldType::Data(_) => quote!(#getter?.to_owned()),
FieldType::Text(_) => quote!(#getter?.to_string()?),
FieldType::Struct(struct_path) => {
Self::Phantom => unimplemented!(),
Self::EnumVariant => unimplemented!(),
Self::Void() => quote!(()),
Self::Primitive(_) => quote!(#getter),
Self::Data(_) => quote!(#getter?.to_owned()),
Self::Text(_) => quote!(#getter?.to_string()?),
Self::Struct(struct_path) | Self::GenericStruct(struct_path) => {
let struct_path = as_turbofish(struct_path);
quote!(#struct_path::read(#getter?)?)
}
FieldType::EnumRemote(_) => quote!(#getter?.into()),
FieldType::Enum(_) => quote!(#getter?),
FieldType::GroupOrUnion(path) => {
Self::EnumRemote(_) => quote!(#getter?.into()),
Self::Enum(_) => quote!(#getter?),
Self::GroupOrUnion(path) => {
let path = as_turbofish(path);
quote!(#path::read(#getter)?)
}
FieldType::UnnamedUnion(union_path) => {
Self::UnnamedUnion(union_path) => {
let union_path = as_turbofish(union_path);
quote!(#union_path::read(#reader_name)?)
}
FieldType::List(item_type) => {
Self::List(item_type) => {
let item_getter = item_type.generate_struct_field_reader_list_item();
quote! {
{
Expand All @@ -400,26 +400,22 @@ impl FieldType {
}
}
}
FieldType::GenericStruct(struct_path) => {
let struct_path = as_turbofish(struct_path);
quote!(#struct_path::read(#getter?)?)
}
}
}

fn generate_struct_field_reader_list_item(&self) -> TokenStream2 {
match self {
FieldType::Void() => quote!(()),
FieldType::Primitive(_) => quote!(reader.get(idx)),
FieldType::Data(_) => quote!(reader.get(idx)?.to_owned()),
FieldType::Text(_) => quote!(reader.get(idx)?.to_string()?),
FieldType::Struct(struct_path) => {
let struct_path = as_turbofish(struct_path);
Self::Void() => quote!(()),
Self::Primitive(_) => quote!(reader.get(idx)),
Self::Data(_) => quote!(reader.get(idx)?.to_owned()),
Self::Text(_) => quote!(reader.get(idx)?.to_string()?),
Self::Struct(struct_path) => {
let struct_path: Path = as_turbofish(struct_path);
quote!(#struct_path::read(reader.get(idx))?)
}
FieldType::EnumRemote(_) => quote!(reader.get(idx)?.into()),
FieldType::Enum(_) => quote!(reader.get(idx)?),
FieldType::List(item_type) => {
Self::EnumRemote(_) => quote!(reader.get(idx)?.into()),
Self::Enum(_) => quote!(reader.get(idx)?),
Self::List(item_type) => {
let item_getter = item_type.generate_struct_field_reader_list_item();
quote! {
{
Expand All @@ -433,7 +429,7 @@ impl FieldType {
}
}
}
FieldType::GenericStruct(struct_path) => {
Self::GenericStruct(struct_path) => {
let struct_path = as_turbofish(struct_path);
quote!(#struct_path::read(reader.get(idx))?)
}
Expand All @@ -454,22 +450,21 @@ impl FieldType {
(quote!(*#field), quote!(#field))
};
match self {
FieldType::Phantom => unimplemented!(),
FieldType::EnumVariant => unimplemented!(),
FieldType::Void() => quote!(builder.#setter(())),
FieldType::Primitive(_) => quote!(builder.#setter(#deref_field)),
FieldType::Data(_) => quote!(builder.#setter(#ref_field)),
FieldType::Text(_) => quote!(builder.#setter(#field.as_str())),
FieldType::Struct(_) => quote!(#field.write(builder.reborrow().#initializer())),
FieldType::EnumRemote(_) => {
Self::Phantom => unimplemented!(),
Self::EnumVariant => unimplemented!(),
Self::Void() => quote!(builder.#setter(())),
Self::Primitive(_) | Self::Enum(_) => quote!(builder.#setter(#deref_field)),
Self::Data(_) => quote!(builder.#setter(#ref_field)),
Self::Text(_) => quote!(builder.#setter(#field.as_str())),
Self::Struct(_) => quote!(#field.write(builder.reborrow().#initializer())),
Self::EnumRemote(_) => {
quote!(builder.#setter(::capnp_conv::RemoteEnum::to_capnp_enum(#ref_field)))
}
FieldType::Enum(_) => quote!(builder.#setter(#deref_field)),
FieldType::GroupOrUnion(_) => {
Self::GroupOrUnion(_) | Self::GenericStruct(_) => {
quote!(#field.write(builder.reborrow().#initializer()))
}
FieldType::UnnamedUnion(_) => quote!(#field.write(builder.reborrow())),
FieldType::List(item_type) => {
Self::UnnamedUnion(_) => quote!(#field.write(builder.reborrow())),
Self::List(item_type) => {
let field_setter = item_type.generate_struct_field_writer_list_item();
quote! {
{
Expand All @@ -482,23 +477,18 @@ impl FieldType {
}
}
}
FieldType::GenericStruct(_) => {
quote!(#field.write(builder.reborrow().#initializer()))
}
}
}
fn generate_struct_field_writer_list_item(&self) -> TokenStream2 {
match self {
FieldType::Void() => quote!(builder.set(idx as u32, ())),
FieldType::Primitive(_) => quote!(builder.set(idx as u32, *item)),
FieldType::Data(_) => quote!(builder.set(idx as u32, item)),
FieldType::Text(_) => quote!(builder.set(idx as u32, item)),
FieldType::Struct(_) => quote!(item.write(builder.reborrow().get(idx as u32))),
FieldType::EnumRemote(_) => {
Self::Void() => quote!(builder.set(idx as u32, ())),
Self::Primitive(_) | Self::Enum(_) => quote!(builder.set(idx as u32, *item)),
Self::Data(_) | Self::Text(_) => quote!(builder.set(idx as u32, item)),
Self::Struct(_) => quote!(item.write(builder.reborrow().get(idx as u32))),
Self::EnumRemote(_) => {
quote!(builder.set(idx as u32, ::capnp_conv::RemoteEnum::to_capnp_enum(item)))
}
FieldType::Enum(_) => quote!(builder.set(idx as u32, *item)),
FieldType::List(item_type) => {
Self::List(item_type) => {
let field_setter = item_type.generate_struct_field_writer_list_item();
quote! {
let list = item;
Expand All @@ -509,7 +499,7 @@ impl FieldType {
}
}
}
FieldType::GenericStruct(_) => {
Self::GenericStruct(_) => {
quote!(item.write(builder.reborrow().get(idx as u32)))
}
_ => unimplemented!(),
Expand Down
39 changes: 19 additions & 20 deletions capnp_conv_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
#![deny(
clippy::nursery,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::string_slice,
clippy::pedantic
)]
#![forbid(unsafe_code)]

mod generators;
mod models;
mod parsers;
Expand All @@ -14,44 +24,33 @@ pub fn capnp_conv(attr_stream: TokenStream, input_stream: TokenStream) -> TokenS
let capnp_struct = parse_macro_input!(attr_stream as Path);
let mut input = parse_macro_input!(input_stream as DeriveInput);

match ItemInfo::parse_input(&input) {
let output = match ItemInfo::parse_input(&input) {
Ok(item_info) => {
let output = item_info.generate_impls(&capnp_struct);
let impls = item_info.generate_impls(&capnp_struct);
remove_capnp_field_attrs(&mut input);
quote! {
#input
#output
#input
#impls
}
}
Err(error) => error.to_compile_error(),
}
.into()
};

output.into()
}

fn remove_capnp_field_attrs(input: &mut DeriveInput) {
match &mut input.data {
syn::Data::Struct(data) => {
for field in &mut data.fields {
drain_filter(&mut field.attrs, is_capnp_attr);
field.attrs.retain(|attr| !is_capnp_attr(attr));
}
}
syn::Data::Enum(data) => {
for variant in &mut data.variants {
drain_filter(&mut variant.attrs, is_capnp_attr);
variant.attrs.retain(|attr| !is_capnp_attr(attr));
}
}
syn::Data::Union(_) => unimplemented!(),
}
}

//not using nightly so we need to do this manually
fn drain_filter<T>(vec: &mut Vec<T>, predicate: fn(&T) -> bool) {
let mut i = 0;
while i != vec.len() {
if predicate(&vec[i]) {
vec.remove(i);
} else {
i += 1;
}
}
}
Loading