Skip to content

Commit

Permalink
doc: vastly improve API docs throughout all crates
Browse files Browse the repository at this point in the history
  • Loading branch information
dnaka91 committed Dec 28, 2023
1 parent 131e7b4 commit afb8a0e
Show file tree
Hide file tree
Showing 15 changed files with 427 additions and 47 deletions.
1 change: 1 addition & 0 deletions crates/stef-build/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use stef_compiler::simplify::{
use super::{decode, encode, size};
use crate::{BytesType, Opts};

/// Take a single schema and convert it into Rust source code.
#[must_use]
pub fn compile_schema(opts: &Opts, Schema { definitions, .. }: &Schema<'_>) -> TokenStream {
let definitions = definitions.iter().map(|def| compile_definition(opts, def));
Expand Down
99 changes: 87 additions & 12 deletions crates/stef-build/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(missing_docs, clippy::missing_errors_doc, clippy::missing_panics_doc)]
//! Code generator crate for Rust projects that can be used in `build.rs` build scripts.

use std::{convert::AsRef, fmt::Debug, path::PathBuf};
use std::{convert::AsRef, env, fmt::Debug, fs, path::PathBuf};

use miette::Report;
use stef_parser::Schema;
Expand All @@ -13,37 +13,89 @@ mod definition;
mod encode;
mod size;

/// Shorthand for the standard result type, that defaults to the crate level's [`Error`] type.
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// Errors that can happen when generating Rust source code from Stef schema files.
#[derive(Error)]
pub enum Error {
/// The required OUT_DIR env var doesn't exist.
#[error("missing OUT_DIR environment variable")]
NoOutDir,
/// One of the user-provided glob patterns is invalid.
#[error("failed to parse the glob pattern {glob:?}")]
Pattern {
/// Source error of the problem.
#[source]
source: glob::PatternError,
/// The problematic pattern.
glob: String,
},
/// Failed to iterate over the matching files for a pattern.
#[error("failed to read files of a glob pattern")]
Glob {
/// Source error of the problem.
#[source]
source: glob::GlobError,
},
/// The file name resulting from a glob pattern didn't produce a usable file path.
#[error("failed to get the file name from a found file path")]
NoFileName,
/// The file name wasn't valid UTF-8.
#[error("the file name was not encoded in valid UTF-8")]
NonUtf8FileName,
/// Failed to create the output directory for generated Rust source files.
#[error("failed creating output directory at {path:?}")]
Create {
/// Source error of the problem.
#[source]
source: std::io::Error,
/// The output directory path.
path: PathBuf,
},
/// Failed to read one of the schema files.
#[error("failed reading schema file at {file:?}")]
Read {
/// Source error of the problem.
#[source]
source: std::io::Error,
/// The problematic file.
file: PathBuf,
},
/// Failed to parse a Stef schema.
#[error("failed parsing schema from {file:?}:\n{report:?}")]
Parse { report: Report, file: PathBuf },
Parse {
/// Detailed report about the problem.
report: Report,
/// The problematic schema file.
file: PathBuf,
},
/// Failed to compile a Stef schema.
#[error("failed compiling schema from {file:?}:\n{report:?}")]
Compile { report: Report, file: PathBuf },
Compile {
/// Detailed report about the problem.
report: Report,
/// The problematic schema file.
file: PathBuf,
},
/// The code generator produced Rust code that isn't valid.
#[error("failed to generate valid Rust code")]
InvalidCode {
/// Source error of the problem.
#[source]
source: syn::Error,
/// The invalid Rust source code.
code: String,
},
/// Failed to write a generated Rust source file.
#[error("failed writing Rust source file to {file:?}")]
Write {
/// Source error of the problem.
#[source]
source: std::io::Error,
/// The problematic file.
file: PathBuf,
},
}

impl Debug for Error {
Expand All @@ -52,36 +104,49 @@ impl Debug for Error {
}
}

/// Instance of the compiler, which is responsible to generate Rust source code from schema files.
#[derive(Default)]
pub struct Compiler {
/// The data type to use for Stef's `bytes` type.
bytes_type: BytesType,
}

/// The data type to use for Stef's `bytes` type, that is used throughout all generated schemas.
#[derive(Clone, Copy, Default)]
pub enum BytesType {
/// Use the default `Vec<u8>` type from Rust's stdlib.
#[default]
VecU8,
/// Use the [`bytes::Bytes`](https://docs.rs/bytes/latest/bytes/struct.Bytes.html) type.
Bytes,
}

/// Additional options to adjust the behavior of the Rust code generator.
#[derive(Default)]
pub struct Opts {
bytes_type: BytesType,
}

impl Compiler {
/// Change the type that is used to represent Stef `bytes` byte arrays.
#[must_use]
pub fn with_bytes_type(mut self, value: BytesType) -> Self {
self.bytes_type = value;
self
}

/// Compile the given list of Stef schema files (glob patterns) into Rust source code.
///
/// # Errors
///
/// Will return an `Err` if any of the various cases happen, which are described in the
/// [`Error`] type.
pub fn compile(&self, schemas: &[impl AsRef<str>]) -> Result<()> {
init_miette();

let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("stef");
let out_dir = PathBuf::from(env::var_os("OUT_DIR").ok_or(Error::NoOutDir)?).join("stef");

std::fs::create_dir_all(&out_dir).map_err(|source| Error::Create {
fs::create_dir_all(&out_dir).map_err(|source| Error::Create {
source,
path: out_dir.clone(),
})?;
Expand All @@ -96,7 +161,7 @@ impl Compiler {
})? {
let path = schema.map_err(|e| Error::Glob { source: e })?;

let input = std::fs::read_to_string(&path).map_err(|source| Error::Read {
let input = fs::read_to_string(&path).map_err(|source| Error::Read {
source,
file: path.clone(),
})?;
Expand All @@ -106,7 +171,11 @@ impl Compiler {
}

for (path, input) in &inputs {
let stem = path.file_stem().unwrap().to_str().unwrap();
let stem = path
.file_stem()
.ok_or(Error::NoFileName)?
.to_str()
.ok_or(Error::NonUtf8FileName)?;

let schema = Schema::parse(input, Some(path)).map_err(|e| Error::Parse {
report: Report::new(e),
Expand Down Expand Up @@ -138,13 +207,19 @@ impl Compiler {
for (stem, schema) in validated {
let schema = stef_compiler::simplify_schema(schema);
let code = definition::compile_schema(&opts, &schema);
let code = prettyplease::unparse(
&syn::parse2(code.clone()).unwrap_or_else(|_| panic!("{code}")),
);
let code = prettyplease::unparse(&syn::parse2(code.clone()).map_err(|source| {
Error::InvalidCode {
source,
code: code.to_string(),
}
})?);

let out_file = out_dir.join(format!("{stem}.rs",));

std::fs::write(out_file, code).unwrap();
fs::write(&out_file, code).map_err(|source| Error::Write {
source,
file: out_file,
})?;
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/stef-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![allow(missing_docs, clippy::missing_errors_doc)]
//! Main command line interface for tooling support of Stef schema files.

use std::{fs, process::ExitCode};

Expand Down
2 changes: 1 addition & 1 deletion crates/stef-compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//! stef_compiler::resolve_schemas(&[("test", &schema)]).unwrap();
//! ```

#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
#![allow(clippy::module_name_repetitions)]

pub use resolve::schemas as resolve_schemas;
pub use simplify::schema as simplify_schema;
Expand Down
4 changes: 4 additions & 0 deletions crates/stef-compiler/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ mod error;
/// schema.
/// - Lastly, the not-found types from the first steps are checked for in the other schemas by
/// utilizing the imports from the second step.
///
/// # Errors
///
/// Will return `Err` if any of the resolution steps fails.
pub fn schemas(values: &[(&str, &Schema<'_>)]) -> Result<(), Error> {
let modules = values
.iter()
Expand Down
8 changes: 7 additions & 1 deletion crates/stef-compiler/src/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ pub struct Schema<'a> {
}

impl Schema<'_> {
/// Render the [JSON Schema](https://json-schema.org/draft-07/json-schema-release-notes) of the complete schema, which can help external tools to understand the structure or derive types from it.
/// Render the [JSON Schema](https://json-schema.org/draft-07/json-schema-release-notes) of the
/// complete schema, which can help external tools to understand the structure or derive types
/// from it.
///
/// # Errors
///
/// Will return `Err` if the schema fails to serialize as JSON string.
#[cfg(feature = "json")]
pub fn json_schema() -> serde_json::Result<String> {
let schema = schemars::schema_for!(Schema<'_>);
Expand Down
4 changes: 4 additions & 0 deletions crates/stef-compiler/src/validate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ impl From<DuplicateFieldName> for Error {
/// - Fields names in structs or enum variants are unique.
/// - Generic type parameters in a struct or enum are unique.
/// - All generic type parameters are used.
///
/// # Errors
///
/// Will return `Err` if any of validation steps fails.
pub fn schema(value: &Schema<'_>) -> Result<(), Error> {
names::validate_names_in_module(&value.definitions)?;
value.definitions.iter().try_for_each(definition)
Expand Down
19 changes: 13 additions & 6 deletions crates/stef-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#![allow(
missing_docs,
clippy::missing_errors_doc,
clippy::module_name_repetitions,
clippy::too_many_lines
)]
//! Internal derive macros for several Stef crates, that reduce boilerplate or create more
//! specialized implementations that the stdlib derives.

#![allow(clippy::module_name_repetitions, clippy::too_many_lines)]

use syn::{parse_macro_input, DeriveInput};

Expand All @@ -12,6 +10,8 @@ mod cause;
mod debug;
mod error;

/// /// Derive the [`miette`](https://docs.rs/miette) and [`winnow`](https://docs.rs/winnow) traits for
/// an error struct that is coupled with a cause enum.
#[proc_macro_derive(ParserError, attributes(err, rename))]
pub fn parser_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
Expand All @@ -22,6 +22,12 @@ pub fn parser_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
}
}

/// Derive the [`miette`](https://docs.rs/miette) and [`winnow`](https://docs.rs/winnow) traits for
/// an error cause enum, which contains one of the possible causes for a failure in parsing.
///
/// The first variant of any enum must be named _Parser_, and contain two unnamed fields with type
/// `ErrorKind` and `usize`. This variant catches generic parser errors from `winnow` and their
/// location.
#[proc_macro_derive(ParserErrorCause, attributes(err, external, forward, rename))]
pub fn parser_error_cause(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
Expand All @@ -32,6 +38,7 @@ pub fn parser_error_cause(input: proc_macro::TokenStream) -> proc_macro::TokenSt
}
}

/// Specialized [`core::fmt::Debug`] macro, which omits span fields from the output.
#[proc_macro_derive(Debug)]
pub fn debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
Expand Down
3 changes: 2 additions & 1 deletion crates/stef-lsp/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Language Server Protocol server implementation for Stef schemas.

#![warn(clippy::expect_used, clippy::unwrap_used)]
#![allow(missing_docs)]

use std::{collections::HashMap, net::Ipv4Addr, time::Instant};

Expand Down
16 changes: 15 additions & 1 deletion crates/stef-parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! The root element is the [`ParseSchemaError`], which forms a tree of errors down to a specific
//! error that caused parsing to fail.

#![allow(missing_docs, clippy::module_name_repetitions)]
#![allow(clippy::module_name_repetitions)]

use std::{
error::Error,
Expand All @@ -27,6 +27,7 @@ pub use crate::parser::{
#[derive(Debug)]
pub struct ParseSchemaError {
pub(crate) source_code: NamedSource,
/// Specific cause of the error.
pub cause: ParseSchemaCause,
}

Expand Down Expand Up @@ -76,11 +77,15 @@ impl Diagnostic for ParseSchemaError {
}
}

/// Specific cause that gives more details about the origin of the error.
#[derive(Debug, Diagnostic)]
pub enum ParseSchemaCause {
/// Non-specific general parser error.
Parser(ErrorKind, usize),
/// Root-level comment for the schema is invalid.
#[diagnostic(transparent)]
Comment(ParseCommentError),
/// Single schema definition is invalid.
#[diagnostic(transparent)]
Definition(ParseDefinitionError),
}
Expand Down Expand Up @@ -133,21 +138,30 @@ where
/// Reason why a single definition was invalid.
#[derive(Debug, Diagnostic)]
pub enum ParseDefinitionError {
/// Non-specific general parser error.
Parser(ErrorKind, usize),
/// Invalid comment section.
#[diagnostic(transparent)]
Comment(ParseCommentError),
/// Invalid element attribute.
#[diagnostic(transparent)]
Attribute(ParseAttributeError),
/// Invalid module definition.
#[diagnostic(transparent)]
Module(ParseModuleError),
/// Invalid struct definition.
#[diagnostic(transparent)]
Struct(ParseStructError),
/// Invalid enum definition.
#[diagnostic(transparent)]
Enum(ParseEnumError),
/// Invalid const definition.
#[diagnostic(transparent)]
Const(ParseConstError),
/// Invalid alias definition.
#[diagnostic(transparent)]
Alias(ParseAliasError),
/// Invalid import definition.
#[diagnostic(transparent)]
Import(ParseImportError),
}
Expand Down
Loading

0 comments on commit afb8a0e

Please sign in to comment.