Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for the const Keyword in Variable Definitions #2

Merged
merged 8 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ const WELCOME_MESSAGE: &str = MESSAGES.info.welcome;
This will read your TOML file and generate Rust data structures accordingly.
Now you can access the values from the TOML file with ease.

In addition to using `static`, the `static_toml!` macro also allows the use of
`const` for embedding TOML data.
This can be particularly useful in scenarios where a constant value is required,
such as within const functions or for const generics.
To use this feature, simply replace `static` with `const` in the macro call when
necessary.

## Customization Options
You can configure how the macro should generate data types:
```rust
Expand Down
39 changes: 39 additions & 0 deletions doc/crate.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,44 @@ static_toml::static_toml! {
}
```

# Using `const`
While the `static_toml!` macro is named for its primary functionality of
statically including TOML files as `static` variables, it also supports the use
of `const` variables.
This option is provided to accommodate specific use cases where `const` is
necessary.

The use of `const` can be crucial in scenarios such as const functions, which
can refer to `const` variables but not to `static` variables, and const generics
that require `const` values.
When deciding between `static` and `const`, it's important to consider the
memory usage implications.
The `static` keyword is ideal for large TOML files that should only occupy space
in the application once, helping to optimize memory usage.
In contrast, using `const` could potentially increase the binary size,
especially if the same TOML data is reused multiple times.
For further details on the `static` keyword in Rust, see the
[Rust documentation on `static`](https://doc.rust-lang.org/std/keyword.static.html).

To use `const` within the `static_toml!` macro, simply replace `static` with
`const` in the macro call.
For example:
```rust
static_toml::static_toml! {
const MESSAGES = include_toml!("messages.toml");
}
```

It's also possible to mix `static` and `const` entries within the same
`static_toml!` macro call.
However, keep in mind that each identifier must be unique within the same scope.
Here's an example of using both `static` and `const` in a single macro call:
```rust
static_toml::static_toml! {
static MESSAGES = include_toml!("messages.toml");
const EXAMPLE = include_toml!("example.toml");
}
```

# Implementation Details
For the specific details, check the documentation for [`static_toml!`].
5 changes: 5 additions & 0 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ static_toml::static_toml! {
)]
#[derive(Debug)]
static EXAMPLE = include_toml!("example.toml");

// constant values also work
#[derive(Debug)]
const MESSAGES = include_toml!("messages.toml");
}

fn main() {
dbg!(&EXAMPLE);
dbg!(MESSAGES);
}
14 changes: 11 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use quote::{format_ident, quote, ToTokens};
use syn::LitStr;
use toml::value::{Table, Value};

use crate::parse::{StaticToml, StaticTomlItem};
use crate::parse::{StaticToml, StaticTomlItem, StorageClass};
use crate::toml_tokens::{fixed_ident, TomlTokens};

mod parse;
Expand Down Expand Up @@ -110,6 +110,11 @@ fn static_toml2(input: TokenStream2) -> Result<TokenStream2, Error> {
)
.map_err(|e| Error::Toml(static_toml.path.clone(), e))?;

let storage_class: &dyn ToTokens = match static_toml.storage_class {
StorageClass::Static(ref token) => token,
StorageClass::Const(ref token) => token
};

// Extract relevant fields from the StaticTomlItem.
let name = &static_toml.name;
let root_type = fixed_ident(
Expand All @@ -128,7 +133,10 @@ fn static_toml2(input: TokenStream2) -> Result<TokenStream2, Error> {
.map(|lit_bool| lit_bool.value),
static_toml.doc.len()
) {
(None, 0) | (Some(true), _) => toml_tokens::gen_auto_doc(&raw_file_path, &content),
(None, 0) | (Some(true), _) => {
toml_tokens::gen_auto_doc(&raw_file_path, &content, &static_toml.storage_class)
}

(None, _) | (Some(false), _) => Default::default()
};

Expand All @@ -143,7 +151,7 @@ fn static_toml2(input: TokenStream2) -> Result<TokenStream2, Error> {
tokens.push(quote! {
#(#doc)*
#auto_doc
#visibility static #name: #root_mod::#root_type = #static_tokens;
#visibility #storage_class #name: #root_mod::#root_type = #static_tokens;

#(#other_attrs)*
#type_tokens
Expand Down
52 changes: 49 additions & 3 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub struct StaticTomlItem {
pub derive: Vec<Attribute>,
/// Visibility of the static value (e.g., `pub`, `pub(crate)`).
pub visibility: Option<Visibility>,
/// Storage class of the variable (`static` or `const`).
pub storage_class: StorageClass,
/// The name of the static value.
pub name: Ident2,
/// The path to the TOML file.
Expand All @@ -48,6 +50,12 @@ pub struct StaticTomlAttributes {
/// A token representing the 'include_toml' keyword.
struct IncludeTomlToken;

/// Storage class for the literal value.
pub enum StorageClass {
Static(Token![static]),
Const(Token![const])
}

/// Parse implementation for `StaticToml`.
///
/// Parses the input into a `StaticToml` struct which contains a vector of
Expand Down Expand Up @@ -135,7 +143,7 @@ impl Parse for StaticTomlItem {
};

// Parse the remainder of the StaticTomlItem.
input.parse::<Token![static]>()?;
let storage_class = input.parse()?;
let name = input.parse()?;
input.parse::<Token![=]>()?;
input.parse::<IncludeTomlToken>()?;
Expand All @@ -151,6 +159,7 @@ impl Parse for StaticTomlItem {
doc,
derive,
visibility,
storage_class,
name,
path
})
Expand All @@ -176,13 +185,46 @@ impl Parse for IncludeTomlToken {
}
}

/// Parse implementation for `StorageClass`.
///
/// Parses the storage classes `static` or `const`.
impl Parse for StorageClass {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.peek(Token![static]) {
return Ok(StorageClass::Static(input.parse::<Token![static]>()?));
}

if input.peek(Token![const]) {
return Ok(StorageClass::Const(input.parse::<Token![const]>()?));
}

Err(input.error("expected `static` or `const`"))
}
}

#[cfg(test)]
mod tests {
use proc_macro2::Span as Span2;
use quote::{format_ident, quote, ToTokens};
use syn::{parse_quote, LitBool, Token, Visibility};

use crate::parse::{IncludeTomlToken, StaticToml, EXPECTED_INCLUDE_TOML};
use crate::parse::{IncludeTomlToken, StaticToml, StorageClass, EXPECTED_INCLUDE_TOML};

impl StorageClass {
fn is_static(&self) -> bool {
match self {
StorageClass::Static(_) => true,
StorageClass::Const(_) => false
}
}

fn is_const(&self) -> bool {
match self {
StorageClass::Static(_) => false,
StorageClass::Const(_) => true
}
}
}

#[test]
fn parse_include_toml_token() {
Expand All @@ -205,7 +247,7 @@ mod tests {
#[derive(PartialEq, Eq)]
#[derive(Default)]
#[static_toml(values_ident = items, suffix = Config, prefer_slices = false)]
pub static CONFIG = include_toml!("config.toml");
pub const CONFIG = include_toml!("config.toml");

/// Documentation comment
#[must_use]
Expand All @@ -225,6 +267,7 @@ mod tests {
assert!(images.other_attrs.is_empty());
assert!(images.derive.is_empty());
assert!(images.visibility.is_none());
assert!(images.storage_class.is_static());
assert_eq!(images.name, format_ident!("IMAGES"));
assert_eq!(images.path.value().as_str(), "images.toml");

Expand All @@ -250,6 +293,7 @@ mod tests {
config.visibility,
Some(Visibility::Public(Token![pub](Span2::call_site())))
);
assert!(config.storage_class.is_const());
assert_eq!(config.name, format_ident!("CONFIG"));
assert_eq!(config.path.value().as_str(), "config.toml");

Expand All @@ -274,6 +318,7 @@ mod tests {
panic!("not a restricted visibility");
};
assert!(example.derive.is_empty());
assert!(example.storage_class.is_static());
assert_eq!(example.name, format_ident!("EXAMPLE"));
assert_eq!(example.path.value().as_str(), "example.toml");

Expand All @@ -286,6 +331,7 @@ mod tests {
assert!(basic.other_attrs.is_empty());
assert!(basic.visibility.is_none());
assert!(basic.derive.is_empty());
assert!(basic.storage_class.is_static());
assert_eq!(basic.name, format_ident!("BASIC"));
assert_eq!(basic.path.value().as_str(), "basic.toml");
}
Expand Down
10 changes: 7 additions & 3 deletions src/toml_tokens/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use syn::{Attribute, Ident as Ident2};
use toml::value::Array;
use toml::Value;

use crate::parse::StaticTomlAttributes;
use crate::parse::{StaticTomlAttributes, StorageClass};

mod static_tokens;
mod type_tokens;
Expand Down Expand Up @@ -216,8 +216,12 @@ fn is_valid_identifier(input: &str) -> bool {
}

/// Generate the auto doc comment for the statics.
pub fn gen_auto_doc(path: &str, content: &str) -> TokenStream2 {
let summary = format!("Static inclusion of `{path}`.");
pub fn gen_auto_doc(path: &str, content: &str, storage_class: &StorageClass) -> TokenStream2 {
let storage_class = match storage_class {
StorageClass::Static(_) => "Static",
StorageClass::Const(_) => "Constant"
};
let summary = format!("{storage_class} inclusion of `{path}`.");
quote! {
#[doc = ""]
#[doc = #summary]
Expand Down
Loading