Skip to content

Commit

Permalink
Merge pull request #2 from cptpiepmatz/define-const
Browse files Browse the repository at this point in the history
Add Support for the `const` Keyword in Variable Definitions
  • Loading branch information
cptpiepmatz authored Nov 25, 2023
2 parents 25208fb + 6877615 commit cdd4a65
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 9 deletions.
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

0 comments on commit cdd4a65

Please sign in to comment.