diff options
author | Inna Palant <ipalant@google.com> | 2024-01-23 23:34:07 +0000 |
---|---|---|
committer | Inna Palant <ipalant@google.com> | 2024-01-23 23:34:07 +0000 |
commit | 6616a966b051ed2cb81d37954b89cdc566b28b1d (patch) | |
tree | 2de2dd96e000c74dce71759c9d2fccc38648bfc5 | |
parent | c6f20e5b30cc35de6429971c43baf8593cb022a8 (diff) | |
parent | 94c81f2ff34ad3a1f26107090df5264561d6df0c (diff) | |
download | p9_wire_format_derive-6616a966b051ed2cb81d37954b89cdc566b28b1d.tar.gz |
Merge remote-tracking branch 'origin/upstream'
Import b/317282804
-rw-r--r-- | .cargo_vcs_info.json | 6 | ||||
-rw-r--r-- | Cargo.toml | 32 | ||||
-rw-r--r-- | Cargo.toml.orig | 16 | ||||
-rw-r--r-- | LICENSE | 27 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | MODULE_LICENSE_BSD_LIKE | 0 | ||||
-rw-r--r-- | OWNERS | 3 | ||||
-rw-r--r-- | README.md | 19 | ||||
-rw-r--r-- | src/lib.rs | 307 |
9 files changed, 429 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..fe5f8f1 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "68f2a1caea21b7707f5c6a992fe5e71f2e89513a" + }, + "path_in_vcs": "p9_wire_format_derive" +}
\ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3c7a2ea --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "p9_wire_format_derive" +version = "0.2.3" +authors = ["The ChromiumOS Authors"] +description = "Supporting proc-macro for the `p9` crate." +readme = "README.md" +license = "BSD-3-Clause" +repository = "https://github.com/google/rust-p9" +resolver = "2" + +[lib] +proc-macro = true + +[dependencies.proc-macro2] +version = "^1" + +[dependencies.quote] +version = "^1" + +[dependencies.syn] +version = "^1" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..d2676bb --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,16 @@ +[package] +name = "p9_wire_format_derive" +version = "0.2.3" +authors = ["The ChromiumOS Authors"] +license = "BSD-3-Clause" +description = "Supporting proc-macro for the `p9` crate." +repository = "https://github.com/google/rust-p9" +readme = "../README.md" + +[dependencies] +syn = "^1" +quote = "^1" +proc-macro2 = "^1" + +[lib] +proc-macro = true @@ -0,0 +1,27 @@ +// Copyright 2017 The ChromiumOS Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..00ab429 --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "p9_wire_format_derive" +description: "Supporting proc-macro for the `p9` crate." +third_party { + identifier { + type: "crates.io" + value: "https://crates.io/crates/p9_wire_format_derive" + } + identifier { + type: "Archive" + value: "https://static.crates.io/crates/p9_wire_format_derive/p9_wire_format_derive-0.2.3.crate" + } + version: "0.2.3" + license_type: NOTICE + last_upgrade_date { + year: 2024 + month: 1 + day: 17 + } +} diff --git a/MODULE_LICENSE_BSD_LIKE b/MODULE_LICENSE_BSD_LIKE new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_BSD_LIKE @@ -0,0 +1,3 @@ +# Bug component: 688011 +include platform/prebuilts/rust:main:/OWNERS +fmayle@google.com diff --git a/README.md b/README.md new file mode 100644 index 0000000..52cd4e6 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# p9 - Server implementation of the [9p] file system protocol + +This directory contains the protocol definition and a server implementation of the [9p] file system +protocol. + +- [wire_format_derive] - A [procedural macro] that derives the serialization and de-serialization + implementation for a struct into the [9p] wire format. +- [src/protocol] - Defines all the messages used in the [9p] protocol. Also implements serialization + and de-serialization for some base types (integers, strings, vectors) that form the foundation of + all [9p] messages. Wire format implementations for all other messages are derived using the + `wire_format_derive` macro. +- [src/server.rs] - Implements a full [9p] server, carrying out file system requests on behalf of + clients. + +[9p]: http://man.cat-v.org/plan_9/5/intro +[procedural macro]: https://doc.rust-lang.org/proc_macro/index.html +[src/protocol]: src/protocol/ +[src/server.rs]: src/server.rs +[wire_format_derive]: wire_format_derive/ diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bc39147 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,307 @@ +// Copyright 2018 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Derives a 9P wire format encoding for a struct by recursively calling +//! `WireFormat::encode` or `WireFormat::decode` on the fields of the struct. +//! This is only intended to be used from within the `p9` crate. + +#![recursion_limit = "256"] + +extern crate proc_macro; +extern crate proc_macro2; + +#[macro_use] +extern crate quote; + +#[macro_use] +extern crate syn; + +use proc_macro2::Span; +use proc_macro2::TokenStream; +use syn::spanned::Spanned; +use syn::Data; +use syn::DeriveInput; +use syn::Fields; +use syn::Ident; + +/// The function that derives the actual implementation. +#[proc_macro_derive(P9WireFormat)] +pub fn p9_wire_format(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + p9_wire_format_inner(input).into() +} + +fn p9_wire_format_inner(input: DeriveInput) -> TokenStream { + if !input.generics.params.is_empty() { + return quote! { + compile_error!("derive(P9WireFormat) does not support generic parameters"); + }; + } + + let container = input.ident; + + let byte_size_impl = byte_size_sum(&input.data); + let encode_impl = encode_wire_format(&input.data); + let decode_impl = decode_wire_format(&input.data, &container); + + let scope = format!("wire_format_{}", container).to_lowercase(); + let scope = Ident::new(&scope, Span::call_site()); + quote! { + mod #scope { + extern crate std; + use self::std::io; + use self::std::result::Result::Ok; + + use super::#container; + + use protocol::WireFormat; + + impl WireFormat for #container { + fn byte_size(&self) -> u32 { + #byte_size_impl + } + + fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> { + #encode_impl + } + + fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> { + #decode_impl + } + } + } + } +} + +// Generate code that recursively calls byte_size on every field in the struct. +fn byte_size_sum(data: &Data) -> TokenStream { + if let Data::Struct(ref data) = *data { + if let Fields::Named(ref fields) = data.fields { + let fields = fields.named.iter().map(|f| { + let field = &f.ident; + let span = field.span(); + quote_spanned! {span=> + WireFormat::byte_size(&self.#field) + } + }); + + quote! { + 0 #(+ #fields)* + } + } else { + unimplemented!(); + } + } else { + unimplemented!(); + } +} + +// Generate code that recursively calls encode on every field in the struct. +fn encode_wire_format(data: &Data) -> TokenStream { + if let Data::Struct(ref data) = *data { + if let Fields::Named(ref fields) = data.fields { + let fields = fields.named.iter().map(|f| { + let field = &f.ident; + let span = field.span(); + quote_spanned! {span=> + WireFormat::encode(&self.#field, _writer)?; + } + }); + + quote! { + #(#fields)* + + Ok(()) + } + } else { + unimplemented!(); + } + } else { + unimplemented!(); + } +} + +// Generate code that recursively calls decode on every field in the struct. +fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream { + if let Data::Struct(ref data) = *data { + if let Fields::Named(ref fields) = data.fields { + let values = fields.named.iter().map(|f| { + let field = &f.ident; + let span = field.span(); + quote_spanned! {span=> + let #field = WireFormat::decode(_reader)?; + } + }); + + let members = fields.named.iter().map(|f| { + let field = &f.ident; + quote! { + #field: #field, + } + }); + + quote! { + #(#values)* + + Ok(#container { + #(#members)* + }) + } + } else { + unimplemented!(); + } + } else { + unimplemented!(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn byte_size() { + let input: DeriveInput = parse_quote! { + struct Item { + ident: u32, + with_underscores: String, + other: u8, + } + }; + + let expected = quote! { + 0 + + WireFormat::byte_size(&self.ident) + + WireFormat::byte_size(&self.with_underscores) + + WireFormat::byte_size(&self.other) + }; + + assert_eq!(byte_size_sum(&input.data).to_string(), expected.to_string()); + } + + #[test] + fn encode() { + let input: DeriveInput = parse_quote! { + struct Item { + ident: u32, + with_underscores: String, + other: u8, + } + }; + + let expected = quote! { + WireFormat::encode(&self.ident, _writer)?; + WireFormat::encode(&self.with_underscores, _writer)?; + WireFormat::encode(&self.other, _writer)?; + Ok(()) + }; + + assert_eq!( + encode_wire_format(&input.data).to_string(), + expected.to_string(), + ); + } + + #[test] + fn decode() { + let input: DeriveInput = parse_quote! { + struct Item { + ident: u32, + with_underscores: String, + other: u8, + } + }; + + let container = Ident::new("Item", Span::call_site()); + let expected = quote! { + let ident = WireFormat::decode(_reader)?; + let with_underscores = WireFormat::decode(_reader)?; + let other = WireFormat::decode(_reader)?; + Ok(Item { + ident: ident, + with_underscores: with_underscores, + other: other, + }) + }; + + assert_eq!( + decode_wire_format(&input.data, &container).to_string(), + expected.to_string(), + ); + } + + #[test] + fn end_to_end() { + let input: DeriveInput = parse_quote! { + struct Niijima_先輩 { + a: u8, + b: u16, + c: u32, + d: u64, + e: String, + f: Vec<String>, + g: Nested, + } + }; + + let expected = quote! { + mod wire_format_niijima_先輩 { + extern crate std; + use self::std::io; + use self::std::result::Result::Ok; + + use super::Niijima_先輩; + + use protocol::WireFormat; + + impl WireFormat for Niijima_先輩 { + fn byte_size(&self) -> u32 { + 0 + + WireFormat::byte_size(&self.a) + + WireFormat::byte_size(&self.b) + + WireFormat::byte_size(&self.c) + + WireFormat::byte_size(&self.d) + + WireFormat::byte_size(&self.e) + + WireFormat::byte_size(&self.f) + + WireFormat::byte_size(&self.g) + } + + fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> { + WireFormat::encode(&self.a, _writer)?; + WireFormat::encode(&self.b, _writer)?; + WireFormat::encode(&self.c, _writer)?; + WireFormat::encode(&self.d, _writer)?; + WireFormat::encode(&self.e, _writer)?; + WireFormat::encode(&self.f, _writer)?; + WireFormat::encode(&self.g, _writer)?; + Ok(()) + } + fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> { + let a = WireFormat::decode(_reader)?; + let b = WireFormat::decode(_reader)?; + let c = WireFormat::decode(_reader)?; + let d = WireFormat::decode(_reader)?; + let e = WireFormat::decode(_reader)?; + let f = WireFormat::decode(_reader)?; + let g = WireFormat::decode(_reader)?; + Ok(Niijima_先輩 { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + g: g, + }) + } + } + } + }; + + assert_eq!( + p9_wire_format_inner(input).to_string(), + expected.to_string(), + ); + } +} |