Skip to content

Commit

Permalink
Add documentation and simplify codegen and macro syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
Virgiel committed Nov 3, 2022
1 parent 9ff15e2 commit 2cc1984
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 117 deletions.
46 changes: 23 additions & 23 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ members = [
"codegen_test",
"bench",
"cornucopia",
"quote"
"code",
]
2 changes: 1 addition & 1 deletion quote/Cargo.toml → code/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "quote"
name = "code"
version = "0.1.0"
edition = "2021"
publish = false
Expand Down
50 changes: 38 additions & 12 deletions quote/src/lib.rs → code/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
use proc_macro::TokenStream;
use unscanny::Scanner;

/// Interpolation kind
enum Pattern<'a> {
Display(&'a str),
Iterator(&'a str),
Call(&'a str),
}

const PATTERN: char = '$';

/// Match allowed character at the beginning of a Rust identifier
fn ident_start(c: char) -> bool {
c.is_alphabetic() || c == '_'
}

/// Match allowed character after the beginning of a Rust identifier
fn ident_body(c: char) -> bool {
c.is_alphanumeric() || c == '_'
}

/// Try to parse a Rust identifier from `scan`
fn parse_ident<'a>(scan: &mut Scanner<'a>) -> Option<&'a str> {
if scan.at(ident_start) {
Some(scan.eat_while(ident_body))
Expand All @@ -34,10 +36,12 @@ fn parse_ident<'a>(scan: &mut Scanner<'a>) -> Option<&'a str> {
}
}

/// Parse the next raw string and optional pattern pair from `scan`
/// Return ("", None) when reach EOF
fn parse_next<'a>(scan: &mut Scanner<'a>) -> (&'a str, Option<Pattern<'a>>) {
let raw = scan.eat_until(PATTERN);
let raw = scan.eat_until('$');

if scan.eat_if(PATTERN) {
if scan.eat_if('$') {
scan.eat_whitespace();
let pattern = if let Some(ident) = parse_ident(scan) {
Some(Pattern::Display(ident))
Expand All @@ -63,13 +67,6 @@ fn parse_next<'a>(scan: &mut Scanner<'a>) -> (&'a str, Option<Pattern<'a>>) {
let inner = scan.from(start);
scan.expect(')');
scan.eat_whitespace();
let mut sep = scan.eat();
if sep != Some('*') {
scan.eat_whitespace();
scan.expect("*");
} else {
sep.take();
}
Some(Pattern::Iterator(inner))
} else {
panic!("Unknown pattern ${}", scan.eat_while(|_| true))
Expand All @@ -80,6 +77,7 @@ fn parse_next<'a>(scan: &mut Scanner<'a>) -> (&'a str, Option<Pattern<'a>>) {
}
}

/// Find all non iterator interpolated identifier in `scan`
fn ident_in_iterator<'a>(scan: &'a mut Scanner) -> Vec<&'a str> {
let start = scan.cursor();
let mut idents = Vec::new();
Expand All @@ -93,6 +91,7 @@ fn ident_in_iterator<'a>(scan: &'a mut Scanner) -> Vec<&'a str> {
idents
}

/// Generate code to write raw `str` into `out`
fn gen_str(s: &mut String, out: &str, str: &str) {
if !str.is_empty() {
s.push_str(out);
Expand All @@ -108,13 +107,15 @@ fn gen_str(s: &mut String, out: &str, str: &str) {
}
}

/// Generate code to write displayable `ident` into `out`
fn gen_disp(s: &mut String, out: &str, ident: &str) {
s.push_str(out);
s.push_str(".write_fmt(format_args!(\"{}\",");
s.push_str(ident);
s.push_str(")).unwrap();\n");
}

/// Generate code to write interpolation patterns in `scan` into `out`
fn gen_recursive<'a>(scan: &'a mut Scanner, s: &mut String, out: &str) {
loop {
let (raw, pattern) = parse_next(scan);
Expand Down Expand Up @@ -165,8 +166,33 @@ fn gen_recursive<'a>(scan: &'a mut Scanner, s: &mut String, out: &str) {
}
}

/// Performs variable interpolation against the input and store the result into
/// a writable output.
///
/// # Display
///
/// You can interpolate any type implementing the [`Display`](std::fmt::Display) trait using `$var`
/// or `${var}`. This grabs the `var` variable that is currently in scope and
/// format it into the output.
///
/// # Lazy
///
/// You can interpolate formatting closure implementing the [`Fn(&mut W)`] trait
/// using `$!lazy` or `$!{lazy}`. This grabs the `lazy` variable that is currently
/// in scope and call it with th output as arg in the right time.
///
/// # Repetition
///
/// Repetition is done using `$(...)`. This iterates through the elements of any variable
/// interpolated within the repetition and inserts a copy of the repetition body
/// for each one. The variables in an interpolation must implement the [`Iterator`] and the
/// [`Clone`] traits.
///
/// - `$($var)` — simple repetition
/// - `$( struct ${var}; )` — the repetition can contain other tokens
/// - `$( $k => println!("{}", $!v), )` — even multiple interpolations
#[proc_macro]
pub fn quote(pattern: TokenStream) -> TokenStream {
pub fn code(pattern: TokenStream) -> TokenStream {
let pattern = pattern.to_string();
let mut scan = unscanny::Scanner::new(&pattern);
scan.eat_whitespace();
Expand Down
8 changes: 4 additions & 4 deletions codegen_test/src/cornucopia_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ pub mod types {
:: accepts(f.type_()),"json" => < cornucopia_async::private::Domain::<&'a serde_json::value::Value> as postgres_types :: ToSql >
:: accepts(f.type_()),"nb" => < cornucopia_async::private::Domain::<i32> as postgres_types :: ToSql >
:: accepts(f.type_()),"arr" => < cornucopia_async::private::Domain::<cornucopia_async::private::DomainArray::<&'a serde_json::value::Value, &[&'a serde_json::value::Value]>> as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
})
}
_ => false,
Expand Down Expand Up @@ -566,7 +566,7 @@ pub mod types {
{
"jsons" => < &'a [&'a serde_json::value::Value] as postgres_types :: ToSql >
:: accepts(f.type_()),"id" => < i32 as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
}
})
}
Expand Down Expand Up @@ -782,7 +782,7 @@ pub mod types {
"wow" => < &'a str as postgres_types :: ToSql >
:: accepts(f.type_()),"such_cool" => < i32 as postgres_types :: ToSql >
:: accepts(f.type_()),"nice" => < super::super::types::public::SpongebobCharacter as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
})
}
_ => false,
Expand Down Expand Up @@ -931,7 +931,7 @@ pub mod types {
"custom" => < &'a [super::super::types::public::CustomCompositeBorrowed<'a>] as postgres_types :: ToSql >
:: accepts(f.type_()),"spongebob" => < &'a [super::super::types::public::SpongebobCharacter] as postgres_types :: ToSql >
:: accepts(f.type_()),"domain" => < cornucopia_async::private::Domain::<&'a str> as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
})
}
_ => false,
Expand Down
8 changes: 4 additions & 4 deletions codegen_test/src/cornucopia_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ pub mod types {
:: accepts(f.type_()),"json" => < cornucopia_sync::private::Domain::<&'a serde_json::value::Value> as postgres_types :: ToSql >
:: accepts(f.type_()),"nb" => < cornucopia_sync::private::Domain::<i32> as postgres_types :: ToSql >
:: accepts(f.type_()),"arr" => < cornucopia_sync::private::Domain::<cornucopia_sync::private::DomainArray::<&'a serde_json::value::Value, &[&'a serde_json::value::Value]>> as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
})
}
_ => false,
Expand Down Expand Up @@ -566,7 +566,7 @@ pub mod types {
{
"jsons" => < &'a [&'a serde_json::value::Value] as postgres_types :: ToSql >
:: accepts(f.type_()),"id" => < i32 as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
}
})
}
Expand Down Expand Up @@ -782,7 +782,7 @@ pub mod types {
"wow" => < &'a str as postgres_types :: ToSql >
:: accepts(f.type_()),"such_cool" => < i32 as postgres_types :: ToSql >
:: accepts(f.type_()),"nice" => < super::super::types::public::SpongebobCharacter as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
})
}
_ => false,
Expand Down Expand Up @@ -929,7 +929,7 @@ pub mod types {
"custom" => < &'a [super::super::types::public::CustomCompositeBorrowed<'a>] as postgres_types :: ToSql >
:: accepts(f.type_()),"spongebob" => < &'a [super::super::types::public::SpongebobCharacter] as postgres_types :: ToSql >
:: accepts(f.type_()),"domain" => < cornucopia_sync::private::Domain::<&'a str> as postgres_types :: ToSql >
:: accepts(f.type_()), _ => false,
:: accepts(f.type_()),_ => false,
})
}
_ => false,
Expand Down
2 changes: 1 addition & 1 deletion cornucopia/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ postgres-types = "0.2.4"
thiserror = "1.0.35"
miette = { version = "5.3.0", features = ["fancy"] }
clap = { version = "3.2.21", features = ["derive"] }
quote = { path = "../quote" }
code = { path = "../code" }
heck = "0.4.0"
indexmap = "1.9.1"
chumsky = "0.8.0"
Loading

0 comments on commit 2cc1984

Please sign in to comment.