cmdparse/lib.rs
1#![doc(html_root_url = "https://docs.rs/cmdparse/0.1.1")]
2#![warn(missing_docs)]
3
4//! `cmdparse` is, as the name suggests, parses user commands into arbitrary Rust types.
5//!
6//! Generally, this crate can be viewed as a data deserialization framework. It defines a syntax
7//! designed to be easy to entered interactively and includes utilities for transforming the input
8//! in this format into arbitrary Rust types, as well as automatically suggesting completions for
9//! incomplete user input.
10//!
11//! It is not suitable for parsing command line arguments, even though the syntax it supports is
12//! fairly similar to what those would look like. Instead, it was designed to be used for parsing
13//! commands entered interactively inside the application. Of course, you are not limited to this
14//! use case and free to use `cmdparse` as a generic data deserialization framework in any way
15//! you like.
16//!
17//! # Examples
18//!
19//! Let’s consider the following example. It defines a struct `MailSendCommand` and derives
20//! [`Parsable`] trait for it. This is enough to be able to parse it.
21//!
22//! ```
23//! use cmdparse::{Parsable, parse};
24//!
25//! #[derive(Debug, PartialEq, Eq, Parsable)]
26//! struct MailSendCommand {
27//! text: String,
28//! #[cmd(attr(subject), default = "\"no subject\".to_string()")]
29//! subject: String,
30//! #[cmd(attr(to))]
31//! to: Vec<String>,
32//! }
33//!
34//! # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
35//! let input = "\"Hello, world\" --to user1@example.com user2@example.com --subject Greeting";
36//! let result = parse::<_, MailSendCommand>(input, ())?;
37//! assert_eq!(result, MailSendCommand {
38//! text: "Hello, world".to_string(),
39//! subject: "Greeting".to_string(),
40//! to: vec!["user1@example.com".to_string(), "user2@example.com".to_string()],
41//! });
42//! # Ok(())
43//! # }
44//! ```
45//!
46//! This example demonstrates several features of `cmdparse`:
47//!
48//! * Parsing functionality can be automatically derived for an arbitrary struct or enum as long
49//! as the inner types are [`Parsable`] or there is an appropriate [`Parser`] for them. (To
50//! learn about the distinction between parsable and parser, read documentation for these traits).
51//! * Derived parser is configurable: you may make fields either required or optional. Optional
52//! fields can be specified via a name attribute (`--` token). They can have a default value
53//! explicitly specified (see default attribute on the `subject` field) or not (`to` field
54//! defaults to an empty vector, as per its [`Default`] implementation)
55//! * Parsable values can contain nested parsable values: `MailSendCommand` is parsable, it
56//! contains a [`Vec`] which is parsable and in repeatedly parses [`String`]s that are parsable.
57//! Note how `cmdparse` recognized that the list of email addresses finished when it
58//! encountered the attribute that neither [`String`] nor [`Vec`] recognizes.
59//!
60//! `cmdparse` can generate completion suggestions:
61//!
62//! ```
63//! # use cmdparse::{Parsable, parse};
64//! use cmdparse::complete;
65//! use std::collections::BTreeSet;
66//!
67//! # #[derive(Debug, PartialEq, Eq, Parsable)]
68//! # struct MailSendCommand {
69//! # text: String,
70//! # #[cmd(attr(subject), default = "\"no subject\".to_string()")]
71//! # subject: String,
72//! # #[cmd(attr(to))]
73//! # to: Vec<String>,
74//! # }
75//! # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
76//! let suggestions = complete::<_, MailSendCommand>("\"Hello, world\" --", ());
77//! assert_eq!(suggestions, BTreeSet::from(["to".into(), "subject".into()]));
78//! # Ok(())
79//! # }
80//! ```
81//!
82//! It also supports parsing enums. In case of enum, it expects a discriminator (automatically
83//! converted into kebab-case by the [`Parsable`] derive macro):
84//!
85//! ```
86//! use cmdparse::{parse, Parsable};
87//!
88//! #[derive(Debug, PartialEq, Eq, Parsable)]
89//! enum Priority {
90//! High,
91//! Medium,
92//! Low,
93//! }
94//!
95//! impl Default for Priority {
96//! fn default() -> Self {
97//! Priority::Medium
98//! }
99//! }
100//!
101//! #[derive(Debug, PartialEq, Eq, Parsable)]
102//! enum Command {
103//! AddTask(String, #[cmd(attr(priority))] Priority),
104//! Remove(usize),
105//! }
106//!
107//! # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
108//! assert_eq!(
109//! parse::<_, Command>("add-task parse-all-commands", ())?,
110//! Command::AddTask("parse-all-commands".to_string(), Priority::Medium),
111//! );
112//! assert_eq!(
113//! parse::<_, Command>("add-task enjoy-your-day --priority high", ())?,
114//! Command::AddTask("enjoy-your-day".to_string(), Priority::High),
115//! );
116//! assert_eq!(parse::<_, Command>("remove 1", ())?, Command::Remove(1));
117//! # Ok(())
118//! # }
119//! ```
120//!
121//! # Syntax
122//!
123//! The syntax that `cmdparse` supports is fairly minimal. The parsing machinery sees the input as
124//! a sequence of tokens. Token is any sequence of characters separated by whitespaces. If you wish
125//! to include a whitespace in the token, you may enclose any substring of the input into a pair of
126//! quotation marks (either double or singular); `cmdparse` supports escaping these symbols
127//! inside quoted tokens with a slash (`\`).
128//!
129//! Input can contain a comment beginning with an octothorp (`#`). Octothorps within quoted tokens
130//! are not considered beginning a comment.
131//!
132//! The meaning of the token and attributes are highly specific to each parser. Generally, each
133//! parser consumes tokens sequentially until each required field’s value is filled. It also
134//! handles attributes in any order and at arbitrary positions.
135//!
136//! Due to the nature of the commands' syntax, parsing can seem ambiguous. For example,
137//! `cmdparse` can parse nested structs such as `Vec<Vec<u32>>`. It may be confusing to the end
138//! user, how would a sequence of numbers be interpreted (they all will be put in the only item of
139//! the outer vector). It is best to design your command to be simple and avoid highly nested
140//! structures for the better user experience. In some cases, complexity is unavoidable. In such
141//! situations, users may find useful an ability to group tokens, belonging to the same data
142//! structure, with parenthesis: `(` and `)`. This way, users can express a value `vec![vec![1, 2],
143//! vec![3, 4, 5]]` as `(1 2) (3 4 5)`.
144//!
145//! More details about how the tokenization and the parsing algorithm are documented in the
146//! [`tokens`] module’s and [`Parser`] trait’s documentation.
147pub mod error;
148pub mod parsers;
149pub mod testing;
150pub mod tokens;
151
152#[doc(hidden)]
153pub use cmdparse_derive::Parsable;
154use error::ParseError;
155use error::ParseFailure;
156use std::borrow::Cow;
157use std::collections::BTreeSet;
158use tokens::TokenStream;
159
160/// The result value returned by the individual parsers
161///
162/// [`Parser::parse`] either succeeds, in which case it returns a value that was parsed and the
163/// remaining tokens in the token stream, or fails with a [`ParseFailure`]. This type alias
164/// represents the return value.
165pub type ParseResult<'a, T> = Result<(T, TokenStream<'a>), ParseFailure<'a>>;
166
167/// Completion suggestions and processing metadata
168///
169/// `CompletionResult` are returned by the [`Parser::complete`] method and are used to indicate
170/// what completions the parser suggests and how other parsers should proceed with the completion
171/// process.
172///
173/// See the documentation for the [`Parser::complete`] method for more information on how values of
174/// this type should be created by the parser.
175#[derive(Debug)]
176pub struct CompletionResult<'a> {
177 /// The token stream with which the parser should continue the completion process
178 ///
179 /// If `remaining` is `None` the completion process should stop. This indicates that the last
180 /// token was encountered, suggestions for it was computed and there is no ambiguity whether
181 /// this token should be consumed by this parser or any other parser. Also, remaining is set to
182 /// `None` in case of an invalid input such that completion is not possible.
183 pub remaining: Option<TokenStream<'a>>,
184
185 /// Indicates whether at least one token was consumed by the parser
186 ///
187 /// Some parsers may behave differently whether the nested parser recognizes any token or not.
188 /// For example if `value_consumed` is `false` the parser may try to handle an attribute that
189 /// is not recognized by the inner parser but may be recognized by the current parser or the
190 /// parent one.
191 pub value_consumed: bool,
192
193 /// The set of suggestions for the last token of a stream
194 ///
195 /// Multiple inner parsers can return suggestions for the same token. Parser should combine all
196 /// of these suggestions into one set.
197 pub suggestions: BTreeSet<Cow<'static, str>>,
198}
199
200impl<'a> CompletionResult<'a> {
201 /// Creates a new `CompletionResult` that indicates that the completion process is finished.
202 ///
203 /// This constructor sets `remaining` as None, and `value_consumed` equal to the `consumed`
204 /// attribute.
205 pub fn new_final(consumed: bool) -> Self {
206 CompletionResult {
207 remaining: None,
208 value_consumed: consumed,
209 suggestions: BTreeSet::new(),
210 }
211 }
212
213 /// Creates a new `CompletionResult` that allows the continuation of the completion process.
214 ///
215 /// This constructor sets the `remaining` and `value_consumed` fields equal to the first and
216 /// second attribute respectively.
217 pub fn new(remaining: TokenStream<'a>, consumed: bool) -> Self {
218 CompletionResult {
219 remaining: Some(remaining),
220 value_consumed: consumed,
221 suggestions: BTreeSet::new(),
222 }
223 }
224
225 /// Updates the `value_consumed` status of the `CompletionResult`.
226 pub fn set_consumed(mut self, consumed: bool) -> Self {
227 self.value_consumed = consumed;
228 self
229 }
230
231 /// Extends the set of completion suggestions.
232 pub fn add_suggestions(
233 mut self,
234 suggestions: impl IntoIterator<Item = Cow<'static, str>>,
235 ) -> Self {
236 self.suggestions.extend(suggestions.into_iter());
237 self
238 }
239}
240
241/// Definition of the parsing and completion algorithm for some type
242///
243/// This trait is fundamental for the functionality of `cmdparse`. The implementers must define
244/// two operations: parsing (converting the input [`TokenStream`] into a value of a target type)
245/// and completion (generating the set of possible completions for the last meaningful token in the
246/// input stream).
247///
248/// Most often, the types being parsed are compound, meaning they contain multiple fields with
249/// different parsers. It is best to keep parsers as simple as possible and delegate most of the
250/// work to the child parsers. To ensure correct interaction between parsers, custom
251/// implementations must follow the parsing protocol. The rules are described in the documentation
252/// for each of the `Parser`'s methods.
253///
254/// Please note, that in most cases writing the parser by hand isn't necessary. Parser is
255/// automatically generated for any type that derives [`Parsable`]. The name of the generated
256/// parser is constructed by appending the word Parser to the end of the type.
257///
258/// # Context
259///
260/// The `Parser` trait is generic over an arbitrary context. Context is passed as an argument to both
261/// of the `Parser`'s methods and is intended to make parsers configurable, meaning their behavior
262/// can depend on some information available at runtime.
263///
264/// The following example demonstrates how to implement the parser for a variant-like data,
265/// dependent on data available at runtime.
266///
267/// ```
268/// use cmdparse::{Parser, CompletionResult, ParseResult, parse_parser, complete_parser};
269/// use cmdparse::tokens::{TokenStream, Token};
270/// use cmdparse::error::{ParseError, UnrecognizedToken};
271/// use std::borrow::Cow;
272/// use std::collections::{BTreeSet, HashMap};
273///
274/// struct RuntimeContext { variables: HashMap<String, u32> }
275///
276/// #[derive(Default)]
277/// struct VariableParser;
278///
279/// impl<'c> Parser<&'c RuntimeContext> for VariableParser {
280/// type Value = u32;
281///
282/// fn parse<'a>(&self, input: TokenStream<'a>, ctx: &'c RuntimeContext) -> ParseResult<'a, Self::Value> {
283/// match input.take().transpose()? {
284/// None => Err(ParseError::token_required().expected("variable").into()),
285/// Some((attr @ Token::Attribute(_), remaining)) => {
286/// Err(UnrecognizedToken::new(attr, remaining).into())
287/// }
288/// Some((token @ Token::Text(text), remaining)) => {
289/// let text = text.parse_string();
290/// match ctx.variables.get(&text as &str) {
291/// Some(value) => Ok((*value, remaining)),
292/// None => Err(UnrecognizedToken::new(token, remaining).into()),
293/// }
294/// }
295/// }
296/// }
297///
298/// fn complete<'a>(&self, input: TokenStream<'a>, ctx: &'c RuntimeContext) -> CompletionResult<'a> {
299/// match input.take() {
300/// Some(Err(_)) | None => CompletionResult::new_final(false),
301/// Some(Ok((Token::Attribute(_), _))) => CompletionResult::new(input, false),
302/// Some(Ok((Token::Text(text), remaining))) if remaining.is_all_consumed() => {
303/// let text = text.parse_string();
304/// CompletionResult::new_final(true).add_suggestions(
305/// ctx.variables.keys()
306/// .filter_map(|key| key.strip_prefix(&text as &str))
307/// .map(|suggestion| Cow::Owned(suggestion.to_string()))
308/// )
309/// }
310/// Some(Ok((Token::Text(_), remaining))) => CompletionResult::new(remaining, true),
311/// }
312/// }
313/// }
314///
315/// # fn main() -> Result<(), ParseError<'static>> {
316/// let context = RuntimeContext {
317/// variables: HashMap::from([("var-1".to_string(), 10), ("var-2".to_string(), 20)]),
318/// };
319///
320/// assert_eq!(parse_parser::<_, VariableParser>("var-1", &context)?, 10);
321/// assert_eq!(parse_parser::<_, VariableParser>("var-2", &context)?, 20);
322/// assert_eq!(
323/// complete_parser::<_, VariableParser>("va", &context),
324/// BTreeSet::from(["r-1".into(), "r-2".into()]),
325/// );
326/// # Ok(())
327/// # }
328/// ```
329///
330/// Parser implementation should be as generic as possible to avoid type errors when integrating
331/// with other parsers.
332pub trait Parser<Ctx>: Default {
333 /// The type that this parser will parse the input stream into.
334 type Value;
335
336 /// Parsers the beginning of the token stream into a `Value`.
337 ///
338 /// This function performs the parsing of the input stream: it repeatedly consumes tokens from
339 /// the token stream and then produces one of the following return values:
340 /// * `Ok((value, remaining))` in case of the correctly parsed sequence of tokens. Here
341 /// `value` is the result of the parsing, (it has type `Self::Value`), and remaining is the
342 /// token stream representing the set of tokens that wasn’t consumed;
343 /// * `Err(ParseFailure::Error(error))` in case the parser failed with an error indicating the
344 /// malformed input. See [`ParseError`];
345 /// * `Err(ParseFailure::Unexpected(unexpected_token))` if the first token in the input stream
346 /// is an attribute or an enum variant discriminator that the parser does not recognize.
347 ///
348 /// To be interoperable with other parsers, the parse implementation must follow the parsing
349 /// protocol:
350 /// * if the first token in the input stream is an attribute and the parser does not recognize
351 /// this attribute, it should return `Err(UnexpectedToken::new(token, remaining).into())`
352 /// where `token` is the attribute that was not recognized, and `remaining` is the token
353 /// stream consisting of tokens directly following token;
354 /// * if the parser expects the enum variant discriminator and the first token of the input is
355 /// not recognized as such, it should return `Err(UnexpectedToken::new(token,
356 /// remaining).into())` with the same values as described above;
357 /// * the parser must not return [`UnexpectedToken`] result with any token other than the first
358 /// token of the input stream; if it receives this value from an inner parser, it must convert
359 /// it into the equivalent error if the parser was not called on the original input;
360 /// * when all required tokens are successfully consumed parser should continue to take tokens
361 /// until a text token or an attribute that is not recognized is encountered (this is not
362 /// necessary if parser does not expect attributes)
363 fn parse<'a>(&self, input: TokenStream<'a>, ctx: Ctx) -> ParseResult<'a, Self::Value>;
364
365 /// Constructs the completion suggestions for the last token of the input stream
366 ///
367 /// It returns a [`CompletionResult`] which contains a set of suggestions and metadata
368 /// instructing how the parent parser should proceed with suggestions generation. If the parser
369 /// calls multiple parsers' `complete` in a row, it should collect the results they produce.
370 ///
371 /// Note, that `complete` produces suggestions for the last token in the stream (if and
372 /// only if there are no characters following it, so only for tokens for which the remaining
373 /// stream's [`is_all_consumed`](TokenStream::is_all_consumed) returns `true`) or when the end
374 /// of stream is reached.
375 ///
376 /// Parser should return:
377 ///
378 /// | Return value | Scenario |
379 /// |------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
380 /// | `CompletionResult::new_final(false)` | The end of the stream is reached, or invalid punctuation is encountered, or the processes cannot continue due to input being invalid sufficiently so that it is not possible to derive its structure. |
381 /// | `CompletionResult::new_final(true)` | The last token is reached and the entirety of the input string is consumed. Parser should include completions for this token if possible. |
382 /// | `CompletionResult::new(input, false)` | The token is the first in the token stream and the parser does not recognize it (following the same protocol described in the documentation for [`parse`](Parser::parse) method). Parser should include completion suggestions that would make token recognizable. |
383 /// | `CompletionReuslt::new(remaining, true)` | The parser successfully consumed all tokens that are required for parsing the value. `remaining` must start with the first non-consumed token, if any. The parser should include suggestions for that non-consumed token. |
384 fn complete<'a>(&self, input: TokenStream<'a>, ctx: Ctx) -> CompletionResult<'a>;
385}
386
387/// Sets the default parser for a given type
388///
389/// This trait allows the users of a type to avoid specifying the parser explicitly.
390///
391/// This trait can be procedurally derived for any struct or enum if all its inner types are
392/// Parsable or have explicit parser specified.
393///
394/// # Derive macro
395///
396/// The `Parsable` derive macro accepts attributes that modify parsing behavior. These attributes
397/// are specified in the form `#[cmd(...)]` attributes can be specifed in the same parenthesis
398/// separated by commas or separately: `#[cmd(default, attr(field))]` and `#[cmd(default)]
399/// #[cmd(attr(field))]` are equivalent.
400///
401/// ## Type attribute
402///
403/// The following attributes are applied to the entire struct or enum for which the trait is being
404/// derived.
405///
406/// ### `ctx = "type-name"`, `ctx_bound = "trait-names"`
407///
408/// Restricts the type of the parsing context in case of ctx attribute or bounds the generic
409/// parsing context to the specific trait or collection of traits in case `ctx_bound` attribute is
410/// used. This is needed when one or more inner parser restricts the type of the context it uses.
411///
412/// The following example demonstrates the creation of a custom parser that requires a specific
413/// parsing context and restricting the context type in the derived trait implementation.
414///
415/// ```
416/// use cmdparse::{parse, tokens::TokenStream, CompletionResult, Parsable, Parser, ParseResult};
417///
418/// #[derive(Debug, Copy, Clone, PartialEq, Eq)]
419/// enum LengthUnit { Cm, In }
420///
421/// #[derive(Clone)]
422/// struct ParsingContext {
423/// unit: LengthUnit,
424/// }
425///
426/// #[derive(Debug, PartialEq)]
427/// struct Length(f64, LengthUnit);
428///
429/// #[derive(Default)]
430/// struct LengthParser;
431///
432/// impl Parser<ParsingContext> for LengthParser {
433/// type Value = Length;
434///
435/// fn parse<'a>(&self, input: TokenStream<'a>, ctx: ParsingContext) -> ParseResult<'a, Self::Value> {
436/// let unit = ctx.unit;
437/// let parser = <f64 as Parsable<ParsingContext>>::Parser::default();
438/// let (value, remaining) = parser.parse(input, ctx)?;
439/// Ok((Length(value, unit), remaining))
440/// }
441///
442/// fn complete<'a>(&self, input: TokenStream<'a>, ctx: ParsingContext) -> CompletionResult<'a> {
443/// let parser = <f64 as Parsable<ParsingContext>>::Parser::default();
444/// parser.complete(input, ctx)
445/// }
446/// }
447///
448/// impl Parsable<ParsingContext> for Length {
449/// type Parser = LengthParser;
450/// }
451///
452/// #[derive(Debug, PartialEq, Parsable)]
453/// #[cmd(ctx = "ParsingContext")]
454/// struct Size {
455/// height: Length,
456/// width: Length,
457/// }
458///
459/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
460/// assert_eq!(
461/// parse::<_, Size>("10 20", ParsingContext{ unit: LengthUnit::Cm })?,
462/// Size {
463/// height: Length(10.0, LengthUnit::Cm),
464/// width: Length(20.0, LengthUnit::Cm)
465/// }
466/// );
467/// # Ok(())
468/// # }
469/// ```
470///
471/// ## Field attributes
472///
473/// The following attributes can be used for the struct’s or enum variant’s fields.
474///
475/// ### `parser = "parser-type-name"`
476///
477/// Specifies a custom parser used for a field.
478///
479/// This attribute is useful in situations where the required parser is different from the parser
480/// defined by the `Parsable` trait (which is used by default) or when implementation of `Parsable`
481/// is not possible (e.g. when dealing with types defined in a foreign crate).
482///
483/// The following example demonstrates how to use `TransformParser` for data validation.
484///
485/// ```
486/// use cmdparse::parsers::{TransformParser, ParsableTransformation};
487/// use cmdparse::error::ParseError;
488/// use cmdparse::Parsable;
489///
490/// struct Number01RangeValidator;
491///
492/// impl ParsableTransformation<f64> for Number01RangeValidator {
493/// type Input = f64;
494///
495/// fn transform(input: Self::Input) -> Result<f64, ParseError<'static>> {
496/// if input < 0.0 || input >= 1.0 {
497/// Err(ParseError::custom("must be between 0 and 1"))
498/// } else {
499/// Ok(input)
500/// }
501/// }
502/// }
503///
504/// #[derive(Debug, Parsable)]
505/// struct Point(
506/// #[cmd(parser = "TransformParser<<f64 as Parsable<CmdParserCtx>>::Parser, Number01RangeValidator, f64>")] f64,
507/// #[cmd(parser = "TransformParser<<f64 as Parsable<CmdParserCtx>>::Parser, Number01RangeValidator, f64>")] f64,
508/// );
509/// ```
510///
511/// ### `default` or `default = "value"` without `attr`
512///
513/// If the `default` attribute is used on a field, this field will not be parsed. Instead, when
514/// constructing the containing instance, the parser uses a default value (if value is not
515/// specified) or a specific value (specified after `=` sign).
516///
517/// ```
518/// use cmdparse::{Parsable, parse};
519///
520/// #[derive(Debug, PartialEq, Eq, Parsable)]
521/// struct MyStruct(#[cmd(default)] u8, #[cmd(default = "5")] u8, u8);
522///
523/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
524/// assert_eq!(parse::<_, MyStruct>("24", ())?, MyStruct(0, 5, 24));
525/// # Ok(())
526/// # }
527/// ```
528///
529/// ### `attr(attribute = "value")` or `attr(attribute)`
530///
531/// Indicates that the field is optional, it can be specified by the user using a named attribute.
532/// This attribute comes in two variants: when “value” is specified, the field’s value is taken
533/// from the expression in the attribute, otherwise the attribute token must be followed by the
534/// field value’s tokens.
535///
536/// ```
537/// use cmdparse::{Parsable, parse};
538///
539/// #[derive(Debug, PartialEq, Eq, Parsable)]
540/// enum Color{ Red, Green, Blue }
541///
542/// impl Default for Color {
543/// fn default() -> Self {
544/// Color::Green
545/// }
546/// }
547///
548/// #[derive(Debug, PartialEq, Eq, Parsable)]
549/// struct MyStruct {
550/// #[cmd(attr(important = "true"))] is_important: bool,
551/// #[cmd(attr(color))] color: Color,
552/// }
553///
554/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
555/// assert_eq!(
556/// parse::<_, MyStruct>("--important", ())?,
557/// MyStruct { color: Color::Green, is_important: true },
558/// );
559/// assert_eq!(
560/// parse::<_, MyStruct>("--color red", ())?,
561/// MyStruct { color: Color::Red, is_important: false },
562/// );
563/// # Ok(())
564/// # }
565/// ```
566///
567/// #### In combination with `default = "value"`
568///
569/// If an optional field’s value is not specified, the default value is used instead, as determined
570/// by the implementation of `Default` trait. This can be overridden by specifying a default value
571/// using `default` attribute.
572///
573/// ```
574/// use cmdparse::{Parsable, parse};
575///
576/// #[derive(Debug, PartialEq, Eq, Parsable)]
577/// struct MyStruct(#[cmd(default = "5", attr(value))] u8);
578///
579/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
580/// assert_eq!(parse::<_, MyStruct>("--value 10", ())?, MyStruct(10));
581/// assert_eq!(parse::<_, MyStruct>("", ())?, MyStruct(5));
582/// # Ok(())
583/// # }
584/// ```
585///
586/// ### `alias_value(alias = "alias", value="value")`
587///
588/// Used for enum variant’s fields. Specifies the value for a field if the specific alias is used
589/// as enum’s discriminator. An `alias` can be either a name of a variant (converted into
590/// kebab-case), a renamed variant name (via `rename` attribute), or an alias defined using `alias`
591/// attribute.
592///
593/// ```
594/// use cmdparse::{Parsable, parse};
595///
596/// #[derive(Debug, PartialEq, Eq, Parsable)]
597/// enum MyEnum {
598/// #[cmd(alias = "enable", alias = "disable")]
599/// SetEnabled(
600/// #[cmd(
601/// alias_value(alias = "enable", value = "true"),
602/// alias_value(alias = "disable", value = "false")
603/// )] bool
604/// )
605/// }
606///
607/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
608/// assert_eq!(parse::<_, MyEnum>("enable", ())?, MyEnum::SetEnabled(true));
609/// assert_eq!(parse::<_, MyEnum>("disable", ())?, MyEnum::SetEnabled(false));
610/// # Ok(())
611/// # }
612/// ```
613///
614/// ## Enum variant attributes
615///
616/// These attributes are applicable to enum variants. Generally, `cmdparse` expects a
617/// discriminator—the variant’s name in kebab-case followed by tokens for its fields if any exist.
618///
619/// ### `rename = "name"`
620///
621/// Changes the name of the variant’s discriminator. The variant cannot be parsed using its
622/// original name.
623///
624/// ```
625/// use cmdparse::{Parsable, parse};
626///
627/// #[derive(Debug, PartialEq, Eq, Parsable)]
628/// enum MyEnum {
629/// #[cmd(rename = "first")] One,
630/// #[cmd(rename = "second")] Two,
631/// }
632///
633/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
634/// assert_eq!(parse::<_, MyEnum>("first", ())?, MyEnum::One);
635/// assert!(parse::<_, MyEnum>("one", ()).is_err());
636/// # Ok(())
637/// # }
638/// ```
639///
640/// ### `alias = "alias"`
641///
642/// Adds an alias for the variant. Variant can have an arbitrary number of aliases and the value
643/// can be parsed using any of this. Specifying an alias does not prevent the usage of the
644/// variant’s original name.
645///
646/// ```
647/// use cmdparse::{Parsable, parse};
648///
649/// #[derive(Debug, PartialEq, Eq, Parsable)]
650/// enum Color {
651/// Black,
652/// White,
653/// #[cmd(alias = "grey")] Gray,
654/// }
655///
656/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
657/// assert_eq!(parse::<_, Color>("grey", ())?, Color::Gray);
658/// assert_eq!(parse::<_, Color>("gray", ())?, Color::Gray);
659/// # Ok(())
660/// # }
661/// ```
662///
663/// ### `ignore`
664///
665/// Disables the parsing of the variant. Note that this does not prevent assigning aliases to the
666/// variant.
667///
668/// ```
669/// use cmdparse::{Parsable, parse};
670///
671/// #[derive(Debug, PartialEq, Eq, Parsable)]
672/// enum MyEnum {
673/// Command,
674/// #[cmd(ignore)] NonInteractive,
675/// }
676///
677/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
678/// assert!(parse::<_, MyEnum>("non-interactive", ()).is_err());
679/// # Ok(())
680/// # }
681/// ```
682///
683/// ### `transparent`
684///
685/// Indicates that a variant can be parsed without a discriminator. This can be used when splitting
686/// a large enum into several smaller ones is desirable.
687///
688/// ```
689/// use cmdparse::{Parsable, parse};
690///
691/// #[derive(Debug, PartialEq, Eq, Parsable)]
692/// enum Subcommand { First, Second }
693///
694/// #[derive(Debug, PartialEq, Eq, Parsable)]
695/// enum Command {
696/// #[cmd(transparent)]
697/// Subcommand(Subcommand),
698/// Third,
699/// }
700///
701/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
702/// assert_eq!(parse::<_, Command>("first", ())?, Command::Subcommand(Subcommand::First));
703/// assert_eq!(parse::<_, Command>("third", ())?, Command::Third);
704/// # Ok(())
705/// # }
706/// ```
707///
708/// ### `transparent_no_error`
709///
710/// Functions similarly to `transparent` but does not terminate parsing on failure. It is useful
711/// when the first field of this variant is not an enum.
712///
713/// ```
714/// use cmdparse::{Parsable, parse};
715///
716/// #[derive(Debug, PartialEq,Parsable)]
717/// enum Value {
718/// #[cmd(transparent_no_error)] Integer(i64),
719/// #[cmd(transparent_no_error)] Real(f64),
720/// #[cmd(transparent_no_error)] Boolean(bool),
721/// }
722///
723/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
724/// assert_eq!(parse::<_, Value>("0.4", ())?, Value::Real(0.4));
725/// assert_eq!(parse::<_, Value>("12", ())?, Value::Integer(12));
726/// assert_eq!(parse::<_, Value>("true", ())?, Value::Boolean(true));
727/// # Ok(())
728/// # }
729/// ```
730///
731/// Note that in the example above, the orders in which the enum variants are declared matters:
732/// `cmdparse` tries to parse transparent variants in order in which they are declared and
733/// returns the first successfully parsed result.
734pub trait Parsable<Ctx> {
735 /// The parser type for this type
736 type Parser: Parser<Ctx, Value = Self>;
737}
738
739/// Parsers a value from an input string using an explicitly specified parser
740///
741/// This function takes an input string slice as an input and a context, and returns a value parsed
742/// from the input or an error. `parse` ensures that all tokens from the input string were
743/// consumed, and the input is valid.
744///
745/// This function is different from [`parse`] in that is expects a parser as its second generic
746/// parameter. The value returned by `parse_parser` does not need to implement [`Parsable`].
747/// `parse_parser` most commonly used with custom parsers.
748///
749/// # Example:
750///
751/// ```
752/// use cmdparse::parse_parser;
753/// use cmdparse::parsers::{IntegerParser, StringParser, tuples::TupleParser2};
754///
755/// type ExplicitParser = TupleParser2<IntegerParser<u64>, StringParser>;
756/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
757/// let value = parse_parser::<_, ExplicitParser>("42 fourty-two", ())?;
758/// assert_eq!(value, (42, "fourty-two".to_string()));
759/// # Ok(())
760/// # }
761/// ```
762pub fn parse_parser<Ctx, P: Parser<Ctx>>(input: &str, ctx: Ctx) -> Result<P::Value, ParseError> {
763 let tokens = TokenStream::new(input);
764 match P::default().parse(tokens, ctx) {
765 Ok((result, remaining)) => match remaining.peek() {
766 Some(Ok(token)) => Err(ParseError::unknown(token)),
767 Some(Err(err)) => Err(err.into()),
768 None => Ok(result),
769 },
770 Err(ParseFailure::Error(err)) => Err(err),
771 Err(ParseFailure::Unrecognized(unrecognized)) => Err(unrecognized.into_error()),
772 }
773}
774
775/// Parsers a [`Parsable`] value from an input string
776///
777/// This function takes an input string slice as an input and a context, and returns a value parsed
778/// from the input or an error. `parse` ensures that all tokens from the input string were
779/// consumed, and the input is valid.
780///
781/// # Example:
782///
783/// ```
784/// use cmdparse::parse;
785///
786/// # fn main() -> Result<(), cmdparse::error::ParseError<'static>> {
787/// let value: (u64, String) = parse("42 fourty-two", ())?;
788/// assert_eq!(value, (42, "fourty-two".to_string()));
789/// # Ok(())
790/// # }
791/// ```
792pub fn parse<Ctx, T: Parsable<Ctx>>(input: &str, ctx: Ctx) -> Result<T, ParseError> {
793 parse_parser::<_, T::Parser>(input, ctx)
794}
795
796/// Computes the completion suggestions for a value using an explicit parser
797///
798/// `compute_parser` takes an input as a string slice with a parsing context and returns a set of
799/// completion suggestions for the last token in the input, if any.
800///
801/// This function is similar to [`complete`] but is expects a parser as its second generic parameter.
802/// It is intended to be used with custom parsers or for types that don’t implement [`Parsable`].
803///
804/// # Examples
805///
806/// ```
807/// use cmdparse::complete_parser;
808/// use cmdparse::parsers::BooleanParser;
809/// use std::collections::BTreeSet;
810///
811/// let suggestions = complete_parser::<_, BooleanParser>("tr", ());
812/// assert_eq!(suggestions, BTreeSet::from(["ue".into()]));
813/// ```
814pub fn complete_parser<Ctx, P: Parser<Ctx>>(input: &str, ctx: Ctx) -> BTreeSet<Cow<'static, str>> {
815 let tokens = TokenStream::new(input);
816 P::default().complete(tokens, ctx).suggestions
817}
818
819/// Computes the completion suggestiosns for a `Parsable` value
820///
821/// `compute` takes an input as a string slice with a parsing context and returns a set of
822/// completion suggestions for the last token in the input if any.
823///
824/// # Examples
825///
826/// ```
827/// use cmdparse::complete;
828/// use std::collections::BTreeSet;
829///
830/// let suggestions = complete::<_, bool>("tr", ());
831/// assert_eq!(suggestions, BTreeSet::from(["ue".into()]));
832/// ```
833pub fn complete<Ctx, T: Parsable<Ctx>>(input: &str, ctx: Ctx) -> BTreeSet<Cow<'static, str>> {
834 let tokens = TokenStream::new(input);
835 T::Parser::default().complete(tokens, ctx).suggestions
836}