Skip to content

Commit

Permalink
feat: add features to disable unused functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
funbiscuit committed Jan 22, 2024
1 parent 2a3b88d commit c135d09
Show file tree
Hide file tree
Showing 19 changed files with 245 additions and 45 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
[![Build Status](https://img.shields.io/github/actions/workflow/status/funbiscuit/embedded-cli-rs/ci.yml?branch=main&style=flat-square)](https://github.com/funbiscuit/embedded-cli-rs/actions/workflows/ci.yml?query=branch%3Amain)
[![Coverage Status](https://img.shields.io/codecov/c/github/funbiscuit/embedded-cli-rs/main?style=flat-square)](https://app.codecov.io/github/funbiscuit/embedded-cli-rs)


[Demo](examples/arduino/README.md) of CLI running on Arduino Nano.
Memory usage: 14KiB of ROM and 0.5KiB of static RAM. Most of static RAM is used by help strings.

Expand Down Expand Up @@ -284,6 +283,7 @@ Commands:
```

Get help for specific command with `help <COMMAND>`:

```
$ help hello
Say hello to World or someone else
Expand All @@ -310,8 +310,10 @@ Options:
```

## User Guide

You'll need to begin communication (usually through a UART) with a device running a CLI.
Terminal is required for correct experience. Following control sequences are supported:

* \r or \n sends a command (\r\n is also supported)
* \b removes last typed character
* \t tries to autocomplete current input
Expand All @@ -320,3 +322,24 @@ Terminal is required for correct experience. Following control sequences are sup

If you run CLI through a serial port (like on Arduino with its UART-USB converter),
you can use for example [PuTTY](https://putty.org) or [tio](https://github.com/tio/tio).

## Memory usage

Memory usage depends on version of crate, enabled features and complexity of your commands.
Below is memory usage of arduino [example](examples/arduino/README.md) when different features are enabled.
Memory usage might change in future versions, but I'll try to keep this table up to date.

| Features | ROM, bytes | Static RAM, bytes |
|---------------------------|:----------:|:-----------------:|
| - | 7494 | 141 |
| autocomplete | 9066 | 161 |
| history | 9168 | 173 |
| help | 10446 | 458 |
| autocomplete+history | 10772 | 193 |
| autocomplete+help | 12052 | 474 |
| help+history | 12154 | 490 |
| autocomplete+help+history | 13968 | 506 |

Commands used to calculate memory usage are given in example [description](examples/arduino/README.md#memory-usage).
As table shows, enabling help adds quite a lot to memory usage since help usually requires a lot of text to be stored.
Also enabling all features almost doubles ROM usage comparing to all features disabled.
2 changes: 2 additions & 0 deletions embedded-cli-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ proc-macro = true

[features]
default = []
autocomplete = []
help = []

[dependencies]
convert_case = "0.6.0"
Expand Down
14 changes: 14 additions & 0 deletions embedded-cli-macros/src/command/autocomplete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use quote::quote;

use super::{model::Command, TargetType};

#[cfg(feature = "autocomplete")]
pub fn derive_autocomplete(target: &TargetType, commands: &[Command]) -> Result<TokenStream> {
let command_count = commands.len();
let command_names: Vec<String> = commands.iter().map(|c| c.name().to_string()).collect();
Expand Down Expand Up @@ -35,3 +36,16 @@ pub fn derive_autocomplete(target: &TargetType, commands: &[Command]) -> Result<

Ok(output)
}

#[allow(unused_variables)]
#[cfg(not(feature = "autocomplete"))]
pub fn derive_autocomplete(target: &TargetType, commands: &[Command]) -> Result<TokenStream> {
let ident = target.ident();
let named_lifetime = target.named_lifetime();

let output = quote! {
impl #named_lifetime _cli::service::Autocomplete for #ident #named_lifetime { }
};

Ok(output)
}
28 changes: 24 additions & 4 deletions embedded-cli-macros/src/command/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use darling::Result;
use proc_macro2::TokenStream;
use quote::quote;

use super::{
model::{Command, CommandArgs},
TargetType,
};
use super::{model::Command, TargetType};

#[cfg(feature = "help")]
use super::model::CommandArgs;

#[cfg(feature = "help")]
pub fn derive_help(
target: &TargetType,
help_title: &str,
Expand Down Expand Up @@ -48,6 +49,24 @@ pub fn derive_help(
Ok(output)
}

#[allow(unused_variables)]
#[cfg(not(feature = "help"))]
pub fn derive_help(
target: &TargetType,
help_title: &str,
commands: &[Command],
) -> Result<TokenStream> {
let ident = target.ident();
let named_lifetime = target.named_lifetime();

let output = quote! {
impl #named_lifetime _cli::service::Help for #ident #named_lifetime { }
};

Ok(output)
}

#[cfg(feature = "help")]
fn create_help_all(commands: &[Command], title: &str) -> Result<TokenStream> {
let max_len = commands.iter().map(|c| c.name().len()).max().unwrap_or(0);
let elements: Vec<_> = commands
Expand All @@ -70,6 +89,7 @@ fn create_help_all(commands: &[Command], title: &str) -> Result<TokenStream> {
})
}

#[cfg(feature = "help")]
fn create_command_help(command: &Command) -> Result<TokenStream> {
let name = command.name();

Expand Down
1 change: 1 addition & 0 deletions embedded-cli-macros/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use self::model::Command;

mod args;
mod autocomplete;
#[cfg(feature = "help")]
mod doc;
mod help;
mod model;
Expand Down
24 changes: 14 additions & 10 deletions embedded-cli-macros/src/command/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{Field, Fields, Variant};

use super::{
args::{ArgType, TypedArg},
doc::Help,
};
use super::args::{ArgType, TypedArg};

#[cfg(feature = "help")]
use super::doc::Help;

#[allow(dead_code)]
#[derive(Debug, FromVariant, Default)]
#[darling(default, attributes(command), forward_attrs(allow, doc, cfg))]
struct CommandAttrs {
Expand All @@ -24,6 +25,7 @@ pub enum CommandArgs {
pub struct CommandArg {
field_name: String,
field_type: TokenStream,
#[cfg(feature = "help")]
help: Help,
ty: ArgType,
}
Expand All @@ -41,20 +43,21 @@ impl CommandArg {
let field_type = aa.inner();
let field_type = quote! { #field_type };

let help = Help::parse(&field.attrs)?;

Ok(Self {
field_name,
field_type,
help,
#[cfg(feature = "help")]
help: Help::parse(&field.attrs)?,
ty,
})
}

#[cfg(feature = "help")]
pub fn help(&self) -> &Help {
&self.help
}

#[cfg(feature = "help")]
pub fn is_optional(&self) -> bool {
self.ty == ArgType::Option
}
Expand All @@ -75,6 +78,7 @@ impl CommandArg {
pub struct Command {
name: String,
args: CommandArgs,
#[cfg(feature = "help")]
help: Help,
ident: Ident,
}
Expand All @@ -84,8 +88,6 @@ impl Command {
let variant_ident = &variant.ident;
let attrs = CommandAttrs::from_variant(variant)?;

let help = Help::parse(&attrs.attrs)?;

let name = attrs.name.unwrap_or_else(|| {
variant_ident
.to_string()
Expand Down Expand Up @@ -117,7 +119,8 @@ impl Command {
Ok(Self {
name,
args,
help,
#[cfg(feature = "help")]
help: Help::parse(&attrs.attrs)?,
ident: variant_ident.clone(),
})
}
Expand All @@ -126,6 +129,7 @@ impl Command {
&self.args
}

#[cfg(feature = "help")]
pub fn help(&self) -> &Help {
&self.help
}
Expand Down
29 changes: 28 additions & 1 deletion embedded-cli-macros/src/group/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use darling::{Error, Result};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use quote::quote;
use syn::{Data, DeriveInput};

#[cfg(feature = "help")]
use quote::format_ident;

use crate::{processor, utils::TargetType};

use self::command_group::CommandGroup;
Expand Down Expand Up @@ -48,6 +51,7 @@ pub fn derive_command_group(input: DeriveInput) -> Result<TokenStream> {
Ok(output)
}

#[cfg(feature = "autocomplete")]
fn derive_autocomplete(target: &TargetType, groups: &[CommandGroup]) -> TokenStream {
let ident = target.ident();
let named_lifetime = target.named_lifetime();
Expand All @@ -74,6 +78,18 @@ fn derive_autocomplete(target: &TargetType, groups: &[CommandGroup]) -> TokenStr
}
}

#[allow(unused_variables)]
#[cfg(not(feature = "autocomplete"))]
fn derive_autocomplete(target: &TargetType, groups: &[CommandGroup]) -> TokenStream {
let ident = target.ident();
let named_lifetime = target.named_lifetime();

quote! {
impl #named_lifetime _cli::service::Autocomplete for #ident #named_lifetime { }
}
}

#[cfg(feature = "help")]
fn derive_help(target: &TargetType, groups: &[CommandGroup]) -> TokenStream {
let ident = target.ident();
let named_lifetime = target.named_lifetime();
Expand Down Expand Up @@ -116,6 +132,17 @@ fn derive_help(target: &TargetType, groups: &[CommandGroup]) -> TokenStream {
}
}

#[allow(unused_variables)]
#[cfg(not(feature = "help"))]
fn derive_help(target: &TargetType, groups: &[CommandGroup]) -> TokenStream {
let ident = target.ident();
let named_lifetime = target.named_lifetime();

quote! {
impl #named_lifetime _cli::service::Help for #ident #named_lifetime { }
}
}

fn derive_from_raw(target: &TargetType, groups: &[CommandGroup]) -> TokenStream {
let ident = target.ident();
let named_lifetime = target.named_lifetime();
Expand Down
5 changes: 4 additions & 1 deletion embedded-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ categories = ["command-line-interface", "embedded", "no-std"]
edition = "2021"

[features]
default = ["macros"]
default = ["macros", "autocomplete", "help", "history"]

macros = ["embedded-cli-macros"]
autocomplete = ["embedded-cli-macros/autocomplete"]
help = ["embedded-cli-macros/help"]
history = []

[dependencies]
embedded-cli-macros = { version = "0.1.0", path = "../embedded-cli-macros", optional = true }
Expand Down
Loading

0 comments on commit c135d09

Please sign in to comment.