~chiefnoah/bare_proc

b3c4e46717f87b9dade5a2aee33df6c9c7b91253 — Noah Pederson 7 months ago f4e0bc4
DOC adds docstrings and small fixes for readme
3 files changed, 113 insertions(+), 5 deletions(-)

M Cargo.lock
M README.md
M src/lib.rs
M Cargo.lock => Cargo.lock +1 -1
@@ 4,7 4,7 @@ version = 3

[[package]]
name = "bare_proc"
version = "0.1.0"
version = "0.1.1"
dependencies = [
 "pest",
 "pest_derive",

M README.md => README.md +1 -1
@@ 30,7 30,7 @@ which will expand roughly the following:
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
struct User {
    name: String,
    key: [u8; 128],
    key: Vec<u8>,
    id: u64,
}
```

M src/lib.rs => src/lib.rs +111 -3
@@ 1,3 1,102 @@
/*!
`bare_proc` provides a simple procedural macro that generates Rust types from BARE schema files.
Generated types implicitly implement `serde::Serialize` and `serde::Deserialize`, as `serde_bare`
is used to handle encoding and decoding. Please see
[serde_bare's documentation](https://docs.rs/serde_bare/latest/serde_bare/) for information on how
the Rust data model maps to the BARE data model.

To use this macro, define a BARE schema file and populate it with type declarations.

For example:


```bare
// schema.bare
type PublicKey data[128]
type Time str # ISO 8601

type Department enum {
  ACCOUNTING
  ADMINISTRATION
  CUSTOMER_SERVICE
  DEVELOPMENT

  # Reserved for the CEO
  JSMITH = 99
}

type Address list<str>[4] # street, city, state, country

type Customer struct {
  name: str
  email: str
  address: Address
  orders: list<struct {
    orderId: i64
    quantity: i32
  }>
  metadata: map<str><data>
}

type Employee struct {
  name: str
  email: str
  address: Address
  department: Department
  hireDate: Time
  publicKey: optional<PublicKey>
  metadata: map<str><data>
}

type TerminatedEmployee void

type Person union {Customer | Employee | TerminatedEmployee}
```

Then, within a Rust source file:

```
use bare_proc::bare_schema;

bare_proc!("schema.bare");


let noah = Employee {
    name: "Noah",
    email: "noah@packetlost.dev",
    address: ["", "", "", ""],
    department: Department::ACCOUNTING,
    hireDate: Vec<u8>,
    publicKey: None,
    metadata: HashMap::new(),
};

```

# BARE => Rust Data Mapping

In most areas, the BARE data model maps cleanly to a Rust representation. Unless otherwise
specified, the most obvious Rust data type is generated from a given BARE type. For example,
a BARE `option<type>` is mapped to Rust's `Option<type>`, BARE unions and enums are mapped to
Rust `enum`s. See below for opinions that this crate has around data types that do not map
as cleanly or require additional explanation.

## Maps

BARE maps are interpreted as `HashMap<K, V>` in Rust. As of now, this is not configurable, but
may be in the future.

## Variable Length Integers

The variable `uint` and `int` types are mapped to [`serde_bare::UInt`] and [`serde_bare::Int`]
respectively. These types wrap `u64` and `i64` (the largest possible sized values stored in BARE
variable length integers).

Arrays that have 32 or less elements are mapped directly as Rust arrays, while BARE arrays with
more than 32 elements are converted into `Vec<T>`.

*/

mod parser;
use std::{collections::BTreeMap, fs::read_to_string};



@@ 13,9 112,15 @@ fn ident_from_string(s: &String) -> Ident {
    Ident::new(s, Span::call_site())
}

/// `bare_schema` parses a BARE schema file and generates equivalent Rust code that is capable of
/// being serialized to and deserialized from bytes using the BARE encoding format. The macro takes
/// exactly one argument, a string that will be parsed as path pointing to a BARE schema file. The
/// path is treated as relative to the file location of the macro's use.
/// For details on how the BARE data model maps to the Rust data model, see the [`Serialize`
/// derive macro's documentation.](https://docs.rs/serde_bare/latest/serde_bare/)
// TODO: add a link to that documentation
#[proc_macro]
pub fn bare_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
    // TODO: support Into<PathBuf>
    let input = parse_macro_input!(item as ExprLit);
    let path = match input.lit {
        syn::Lit::Str(s) => s,


@@ 32,6 137,7 @@ pub fn bare_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
        use std::io::{Error as IOError, Read, Write};
        use serde::{Serialize, Deserialize};
        use std::collections::HashMap;
        use serde_bare::{UInt, Int};

        #(#user_type_syntax)*
    }


@@ 312,11 418,13 @@ fn gen_anonymous(
fn gen_primative_type_def(p: &PrimativeType) -> TokenStream {
    use PrimativeType::*;
    match p {
        UInt | U64 => quote! { u64 },
        UInt => quote! { UInt },
        U64 => quote! { u64 },
        U32 => quote! { u32 },
        U16 => quote! { u16 },
        U8 => quote! { u8 },
        Int | I64 => quote! { i64 },
        Int => quote! { Int },
        I64 => quote! { i64 },
        I32 => quote! { i32 },
        I16 => quote! { i16 },
        I8 => quote! { i8 },