Skip to content

Commit

Permalink
Added a _lot_ of documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
VictorKoenders committed Dec 4, 2021
1 parent 11bc467 commit 4fe3a5b
Show file tree
Hide file tree
Showing 13 changed files with 451 additions and 175 deletions.
55 changes: 49 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
use crate::{generate::StreamBuilder, prelude::*};
use crate::{
generate::{PushParseError, StreamBuilder},
prelude::*,
};
use std::fmt;

/// Errors that can occur while parsing or generator your derive macro.
#[derive(Debug)]
pub enum Error {
/// The data type at `Span` is unknown. This will be called when [`Parse::new`] is called on anything that is not a `struct` or `enum`.
///
/// [`Parse::new`]: enum.Parse.html#method.new
UnknownDataType(Span),
InvalidRustSyntax { span: Span, expected: String },

/// The rust syntax is invalid. This can be returned while parsing the Enum or Struct.
///
/// This error is assumed to not appear as rustc will do syntax checking before virtue gets access to the [`TokenStream`].
/// However this error could still be returned.
InvalidRustSyntax {
/// The span at which the invalid syntax is found
span: Span,
/// The expected rust syntax when this parsing occured
expected: String,
},

/// Expected an ident at the given span.
ExpectedIdent(Span),

/// Failed to parse the code passed to [`StreamBuilder::push_parsed`].
///
/// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed
PushParse(PushParseError),
}

impl From<PushParseError> for Error {
fn from(e: PushParseError) -> Self {
Self::PushParse(e)
}
}

impl Error {
pub fn wrong_token<T>(token: Option<&TokenTree>, expected: &str) -> std::result::Result<T, Self> {
pub(crate) fn wrong_token<T>(
token: Option<&TokenTree>,
expected: &str,
) -> std::result::Result<T, Self> {
Err(Self::InvalidRustSyntax {
span: token.map(|t| t.span()).unwrap_or_else(Span::call_site),
expected: format!("{}, got {:?}", expected, token),
Expand Down Expand Up @@ -39,20 +72,30 @@ impl fmt::Display for Error {
write!(fmt, "Invalid rust syntax, expected {}", expected)
}
Self::ExpectedIdent(_) => write!(fmt, "Expected ident"),
Self::PushParse(e) => write!(
fmt,
"Invalid code passed to `StreamBuilder::push_parsed`: {:?}",
e
),
}
}
}

impl Error {
/// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user.
pub fn into_token_stream(self) -> TokenStream {
let maybe_span = match &self {
Error::UnknownDataType(span)
| Error::ExpectedIdent(span)
| Error::InvalidRustSyntax { span, .. } => Some(*span),
Self::UnknownDataType(span)
| Self::ExpectedIdent(span)
| Self::InvalidRustSyntax { span, .. } => Some(*span),
// PushParseError.error technically has a .span(), but this will be the span in the users derive impl
// so we pretend to not have a span
Self::PushParse(_) => None,
};
self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site))
}

/// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user. The error will be shown at the given `span`.
pub fn throw_with_span(self, span: Span) -> TokenStream {
// compile_error!($message)
let mut builder = StreamBuilder::new();
Expand Down
3 changes: 3 additions & 0 deletions src/generate/generate_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ impl<'a, 'b> FnBuilder<'a, 'b> {
/// builder.body(|b| {
/// b.push_parsed("println!(\"hello world\");");
/// });
/// // fn foo() {
/// // println!("Hello world");
/// // }
/// ```
pub fn body(self, body_builder: impl FnOnce(&mut StreamBuilder)) -> Result<(), PushParseError> {
let FnBuilder {
Expand Down
8 changes: 5 additions & 3 deletions src/generate/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use crate::prelude::{Ident, TokenStream};

#[must_use]
/// The generator is used to generate code.
///
///
/// Often you will want to use [`impl_for`] to generate an `impl <trait_name> for <target_name()>`.
///
/// [`impl_for`]: #method.impl_for
pub struct Generator {
pub(crate) name: Ident,
pub(crate) generics: Option<Generics>,
Expand Down Expand Up @@ -48,8 +50,8 @@ impl Generator {
}

/// Consume the contents of this generator. This *must* be called, or else the generator will panic on drop.
pub fn take_stream(mut self) -> TokenStream {
std::mem::take(&mut self.stream).stream
pub fn finish(mut self) -> crate::prelude::Result<TokenStream> {
Ok(std::mem::take(&mut self.stream).stream)
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/generate/impl_for.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ impl<'a> ImplFor<'a> {
}

/// Add a function to the trait implementation.
///
///
/// `generator.impl_for("Foo").generate_fn("bar")` results in code like:
///
///
/// ```ignore
/// impl Foo for <struct or enum> {
/// fn bar() {}
/// }
/// ```
///
///
/// See [`FnBuilder`] for more options, as well as information on how to fill the function body.
pub fn generate_fn<'b>(&'b mut self, name: &str) -> FnBuilder<'a, 'b> {
FnBuilder::new(self, name)
Expand Down
16 changes: 15 additions & 1 deletion src/generate/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
//! Code to help generate functions.
//!
//! The structure is:
//!
//! - [`Generator`]
//! - `.impl_for()`: [`ImplFor`]
//! - `.generate_fn()`: [`FnBuilder`]
//! - `.body(|builder| { .. })`: [`StreamBuilder`]
//!
//! Afterwards, [`Generator::finish()`] **must** be called to take out the [`TokenStream`] produced.
//!
//! [`Generator::finish()`]: struct.Generator.html#method.finish
//! [`TokenStream`]: ../prelude/struct.TokenStream.html
mod generate_fn;
mod generator;
mod impl_for;
Expand All @@ -6,4 +20,4 @@ mod stream_builder;
pub use self::generate_fn::{FnBuilder, FnSelfArg};
pub use self::generator::Generator;
pub use self::impl_for::ImplFor;
pub use self::stream_builder::StreamBuilder;
pub use self::stream_builder::{PushParseError, StreamBuilder};
11 changes: 9 additions & 2 deletions src/generate/stream_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ impl StreamBuilder {

/// Add a single punctuation to the stream. Puncts are single-character tokens like `.`, `<`, `#`, etc
///
/// Note that this should not be used for multi-punct constructions like `::` or `->`. For that use [puncts] instead.
/// Note that this should not be used for multi-punct constructions like `::` or `->`. For that use [`puncts`] instead.
///
/// [`puncts`]: #method.puncts
pub fn punct(&mut self, p: char) {
self.stream
.extend([TokenTree::Punct(Punct::new(p, Spacing::Alone))]);
Expand All @@ -80,7 +82,7 @@ impl StreamBuilder {
/// Add multiple punctuations to the stream. Multi punct tokens are e.g. `::`, `->` and `=>`.
///
/// Note that this is the only way to add multi punct tokens.
/// If you were to use [punct] to insert `->` it would be inserted as `-` and then `>`, and not form a single token. Rust would interpret this as a "minus sign and then a greater than sign", not as a single arrow.
/// If you were to use [`Punct`] to insert `->` it would be inserted as `-` and then `>`, and not form a single token. Rust would interpret this as a "minus sign and then a greater than sign", not as a single arrow.
pub fn puncts(&mut self, puncts: &str) {
self.stream.extend(
puncts
Expand Down Expand Up @@ -143,8 +145,13 @@ impl StreamBuilder {
}
}

/// Failed to parse the code passed to [`StreamBuilder::push_parsed`]
///
/// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed
#[derive(Debug)]
pub struct PushParseError {
/// The parsing error
pub error: LexError,
/// The code that was being parsed
pub code: String,
}
79 changes: 72 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,98 @@
//! # Virtue, a sinless derive macro helper
//!
//! ## Usage
//!
//!
//! ## Goals
//!
//! - Zero dependencies, so fast compile times
//! - No other dependencies needed
//! - Declarative code generation
//! - As much typesystem checking as possible
//! - Build for modern rust: 1.57 and up
//! - Build with popular crates in mind:
//! - [bincode](https://docs.rs/bincode)
//! - Will always respect semver. Minor releases will never have:
//! - Breaking API changes
//! - MSRV changes
//!
//! ## Example
//!
//! ```ignore
//! use virtue::prelude::*;
//!
//! #[proc_macro_derive(YourDerive, attributes(some, attributes, go, here))]
//! pub fn derive_encode(input: TokenStream) -> TokenStream {
//! pub fn derive_your_derive(input: TokenStream) -> TokenStream {
//! derive_your_derive_inner(input)
//! .unwrap_or_else(|error| error.into_token_stream())
//! }
//!
//! fn derive_your_derive_inner(input: TokenStream) -> Result<TokenStream> {
//! // Parse the struct or enum you want to implement a derive for
//! let parse = Parse::new(input)?;
//! // Get a reference to the generator
//! let (mut generator, body) = parse.into_generator();
//! match body {
//! Body::Struct(body) => {
//! // Implement your struct body here
//! // See `Generator` for more information
//! generator.impl_for("YourTrait")?
//! .generate_fn("your_fn")
//! .with_self_arg(FnSelfArg::RefSelf)
//! .body(|fn_body| {
//! fn_body.push_parsed("println!(\"Hello world\");");
//! })?;
//! },
//! Body::Enum(body) => {
//! // Implement your enum body here
//! // See `Generator` for more information
//! generator.impl_for("YourTrait")?
//! .generate_fn("your_fn")
//! .with_self_arg(FnSelfArg::RefSelf)
//! .body(|fn_body| {
//! fn_body.push_parsed("println!(\"Hello world\");");
//! })?;
//! },
//! }
//! generator.finish()
//! }
//! ```
//!
//! Will generate
//!
//! ```ignore
//! impl YourTrait for <Struct or Enum> {
//! fn your_fn(&self) { // .generate_fn("your_fn").with_self_arg(FnSelfArg::RefSelf)
//! println!("Hello world"); // fn_body.push_parsed(...)
//! }
//! }
//! ```
#![warn(missing_docs)]


mod error;

pub mod generate;
pub mod parse;

/// Result alias for virtue's errors
pub type Result<T = ()> = std::result::Result<T, Error>;

pub use self::error::Error;

#[cfg(test)]
/// Useful includes
pub mod prelude {
pub use proc_macro2::*;
pub use crate::generate::FnSelfArg;
pub use crate::parse::{Body, Parse};
pub use crate::Result;
pub use proc_macro2::*;
}
#[cfg(not(test))]
/// Useful includes
pub mod prelude {
extern crate proc_macro;

pub use proc_macro::*;
pub use crate::generate::FnSelfArg;
pub use crate::parse::{Body, Parse};
pub use crate::Result;
pub use proc_macro::*;
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 4fe3a5b

Please sign in to comment.