Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Leonard committed Sep 23, 2023
0 parents commit d77d288
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Rust CI

on:
push:
branches:
- master

pull_request:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Check
run: cargo check --all --examples --tests

- name: Format
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --workspace --examples --tests -- -D warnings
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
/Cargo.lock
.idea
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "dry-mods"
description = "Macros to make your module management DRY"
version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/tigerros/dry-mods"
categories = ["development-tools", "development-tools:build-utils", "rust-patterns", "no-std", "no-std:no-alloc"]
keywords = ["macros", "no-std", "module", "DRY", "no-alloc"]
include = ["src", "README.md"] # Don't include examples in crate
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# DRY modules

Don't Repeat Yourself, be DRY!
That's why you should use this crate, for removing repetition and verboseness from your module declarations.

Check out the docs for a guide and examples.

Why this crate?
- Intuitive macro syntax.
- Extremely lightweight. No dependencies.
- You can use it in a `no-std` or `no-alloc` context.
3 changes: 3 additions & 0 deletions examples/bar/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn bar() {
println!("bar");
}
3 changes: 3 additions & 0 deletions examples/foo/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn foo() {
println!("foo");
}
19 changes: 19 additions & 0 deletions examples/mods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
mod baz {
pub(crate) fn baz() {
println!("baz");
}
}

dry_mods::mods! {
// `foo` and `bar` will be defined with the `pub(crate)` visibility but the contents will be completely public.
pub(crate) mod pub use foo, bar;
// `baz` is totally private, but the contents are exposed in the module where `mods!` was called.
// We don't use `mod` here, because `baz` is already defined.
use baz;
}

fn main() {
foo();
bar();
baz();
}
27 changes: 27 additions & 0 deletions examples/prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mod internal1 {
pub(crate) fn int1() {
println!("int1");
}
}

mod internal2 {
pub(crate) fn int2() {
println!("int2");
}
}

dry_mods::prelude! {
// `foo` and `bar` will be public modules and their contents will also be public.
/// Re-exports some commonly used modules.
pub mod pub use foo, bar;
// `internal1` and `internal2` will only be visible within the crate.
// We don't use `mod` here, because they are already defined.
pub(crate) use crate::{internal1, internal2};
}

fn main() {
prelude::foo();
prelude::bar();
prelude::int1();
prelude::int2();
}
170 changes: 170 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//! Compiled examples are in the [repository](https://github.com/tigerros/dry-mods/tree/master/examples).

#![warn(clippy::cargo)]
#![warn(clippy::pedantic)]
#![warn(clippy::style)]
#![warn(clippy::nursery)]
#![no_std]
#![no_main]

/// Flexible and powerful module declarations.
///
/// # Syntax
/// There's three different patterns the macro matches against:
/// - Define modules and use the contents (`::*`).
/// - Define modules.
/// - Use the contents of modules that are already in scope.
/// - Use the contents of modules that are in a root module.
/// For example, `crate::{mod1, mod2}` instead of `crate::mod1, crate::mod2`.
///
/// After each pattern, you can type a semicolon (`;`) and make another one!
///
/// # Examples
/// This showcases each of the three patterns:
/// ```rs
/// dry_mods::mods! {
/// // `foo` and `bar` will be defined with the `pub(crate)` visibility but the contents will be completely public.
/// pub(crate) mod pub use foo, bar;
/// // `baz` is totally private, but the contents are exposed in the module where `mods!` was called.
/// use baz;
/// // `my_mod` is now a public module.
/// pub mod my_mod;
/// // Use the root module for crate internals.
/// pub(crate) use crate::{int1, int2, int3, int4, int5};
/// }
///
/// // Generates:
///
/// pub(crate) mod foo;
/// pub(crate) mod bar;
/// pub use foo::*;
/// pub use bar::*;
/// use baz::*;
/// pub mod my_mod;
/// pub(crate) crate::{int1::*, int2::*, int3::*, int4::*, int5::*};
/// ```
#[macro_export]
macro_rules! mods {
() => {};

($mod_vis:vis mod $use_vis:vis use $($module:ident),+; $($rest:tt)*) => {
$($mod_vis mod $module;)+
$($use_vis use $module::*;)+
::dry_mods::mods! { $($rest)* }
};

($mod_vis:vis mod $($module:ident),+; $($rest:tt)*) => {
$($mod_vis mod $module;)+
::dry_mods::mods! { $($rest)* }
};

// No root.
($use_vis:vis use $($mod_path:path),+; $($rest:tt)*) => {
$($use_vis use $mod_path::*;)+
::dry_mods::mods! { $($rest)* }
};

// Yes root.
($use_vis:vis use $root:ident::{$($mod_path:path),+}; $($rest:tt)*) => {
$($use_vis use $root::{$mod_path::*};)+
::dry_mods::mods! { $($rest)* }
};
}

/// Generates a `prelude` module with some uses.
///
/// # Syntax
/// At the start of each pattern, you can add attributes that will then be added to the `prelude`
/// module. You can also write documentation!
///
/// There's three different patterns the macro matches against.
/// "use" means "use in the `mod prelude`".
/// - Define modules in the file and use the contents (`::*`).
/// This will use `super::mod_name` to get the modules in the file.
/// - Use the contents of modules that are already in scope.
/// - Use the contents of modules that are in a root module.
/// For example, `crate::{mod1, mod2}` instead of `crate::mod1, crate::mod2`.
///
/// After each pattern, you can type a semicolon (`;`) and make another one!
///
/// # Examples
/// This showcases each of the three patterns:
/// ```rs
/// mod internal1 {
/// pub(crate) fn int1() {}
/// }
///
/// mod internal2 {
/// pub(crate) fn int2() {}
/// }
///
/// dry_mods::prelude! {
/// // `foo` and `bar` will be public modules and their contents will also be public.
/// /// Re-exports some commonly used modules.
/// pub mod pub use foo, bar;
/// // `internal1` and `internal2` will only be visible within the crate.
/// // We don't use `mod` here, because they are already defined.
/// pub(crate) use crate::{internal1, internal2};
/// }
///
/// // Generates:
///
/// pub mod foo;
/// pub mod bar;
/// #[doc = " Re-exports some commonly used modules."]
/// pub mod prelude {
/// pub use super::foo::*;
/// pub use super::bar::*;
/// pub(crate) use crate::{internal1::*, internal2::* };
/// }
/// ```
#[macro_export]
macro_rules! prelude {
() => {};

// Using modules in the prelude by prepending "U="
(U=) => {};

// No use root.
(U=$vis:vis use $($mod_path:path),+; $($rest:tt)*) => {
$($vis use $mod_path::*;)+
::dry_mods::prelude! { U=$($rest)* }
};

// Yes use root.
(U=$vis:vis use $root:ident::{$($mod_path:path),+}; $($rest:tt)*) => {
$vis use $root::{$($mod_path::*,)+};
::dry_mods::prelude! { U=$($rest)* }
};

// Prelude, mod and use.
($(#[$attr:meta])* $mod_vis:vis mod $use_vis:vis use $($module:ident),+; $($rest:tt)*) => {
$($mod_vis mod $module;)*
$(#[$attr])
*
$use_vis mod prelude {
$($use_vis use super::$module::*;)*
::dry_mods::prelude! { U=$($rest)* }
}
};

// Prelude, use without root.
($(#[$attr:meta])* $use_vis:vis use $($mod_path:path),+; $($rest:tt)*) => {
$(#[$attr])
*
$use_vis mod prelude {
$($use_vis use $mod_path::*;)+
::dry_mods::prelude! { U=$($rest)* }
}
};

// Prelude, use with root.
($(#[$attr:meta])* $use_vis:vis use $root:ident::{$($mod_path:path),+}; $($rest:tt)*) => {
$(#[$attr])
*
$use_vis mod prelude {
$($use_vis use $root::{$mod_path::*};)+
::dry_mods::prelude! { U=$($rest)* }
}
};
}

0 comments on commit d77d288

Please sign in to comment.