Skip to content
Draft
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
4 changes: 2 additions & 2 deletions src/de/app/color.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use clap::ColorChoice;
use serde::{de::DeserializeSeed, Deserialize};
use serde::{de::DeserializeSeed, Deserialize, Serialize};

enum_de!(ColorChoice,ColorChoice1,
#[derive(Deserialize, Clone, Copy)]
#[derive(Deserialize, Serialize, Clone, Copy)]
#[cfg_attr(feature = "kebab-case-key" ,serde(rename_all = "kebab-case"))]
#[cfg_attr(feature = "snake-case-key" ,serde(rename_all = "snake_case"))]
{
Expand Down
53 changes: 50 additions & 3 deletions src/de/app/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,60 @@
use crate::CommandWrap;
use appsettings::*;
use clap::Command;
use serde::{
de::{DeserializeSeed, Error, Visitor},
Deserialize,
};
use std::ops::Deref;

mod appsettings;
#[cfg(feature = "color")]
mod color;
pub(crate) mod color;

/// Wrapper of [`Command`] to serialzie and deserialize.
/// ```
/// const CLAP_TOML: &'static str = r#"
/// name = "app_clap_serde"
/// version = "1.0"
/// author = "tester"
/// about = "test-clap-serde"
/// "#;
/// let app: clap::Command = toml::from_str::<clap_serde::CommandWrap>(CLAP_TOML)
/// .expect("parse failed")
/// .into();
/// assert_eq!(app.get_name(), "app_clap_serde");
/// assert_eq!(app.get_about(), Some("test-clap-serde"));
/// ```
#[derive(Debug, Clone)]
pub struct CommandWrap<'a> {
pub(crate) app: Command<'a>,
}

impl<'a> CommandWrap<'a> {
/// Create a wrapper for [`Command`].
pub fn new(app: Command<'a>) -> Self {
Self { app }
}
}

impl<'a> From<CommandWrap<'a>> for Command<'a> {
fn from(a: CommandWrap<'a>) -> Self {
a.app
}
}

impl<'a> From<Command<'a>> for CommandWrap<'a> {
fn from(app: Command<'a>) -> Self {
CommandWrap { app }
}
}

impl<'a> Deref for CommandWrap<'a> {
type Target = Command<'a>;

fn deref(&self) -> &Self::Target {
&self.app
}
}

const TMP_APP_NAME: &str = "__tmp__deserialize__name__";
impl<'de> Deserialize<'de> for CommandWrap<'de> {
Expand Down Expand Up @@ -110,6 +156,7 @@ impl<'a> Visitor<'a> for CommandVisitor<'a> {
// subcommands : specialized
(subcommand_help_heading, &str),
(subcommand_negates_reqs, bool),
(subcommand_precedence_over_arg, bool),
(subcommand_required, bool),
(subcommand_value_name, &str),
(propagate_version, bool),
Expand Down Expand Up @@ -158,7 +205,7 @@ impl<'a> Visitor<'a> for CommandVisitor<'a> {
]);
}

Ok(CommandWrap { app })
Ok(app.into())
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/de/arg/arg_action.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use clap::ArgAction as AA;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

enum_de!(AA, ArgAction,
#[derive(Deserialize, Clone, Copy)]
#[derive(Serialize, Deserialize, Clone, Copy)]
#[cfg_attr(feature = "kebab-case-key" ,serde(rename_all = "kebab-case"))]
#[cfg_attr(feature = "snake-case-key" ,serde(rename_all = "snake_case"))]
{
Expand Down
43 changes: 38 additions & 5 deletions src/de/arg/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
use self::{arg_action::ArgAction, value_hint::ValueHint, value_parser::ValueParser};
use crate::ArgWrap;
use clap::{Arg, Command};
use serde::de::{DeserializeSeed, Error, Visitor};
use std::marker::PhantomData;
use std::{marker::PhantomData, ops::Deref};

mod arg_action;
mod value_hint;
pub(crate) mod arg_action;
pub(crate) mod value_hint;
mod value_parser;

/// Wrapper of [`Arg`] to deserialize with [`DeserializeSeed`](`serde::de::DeserializeSeed`).
#[derive(Debug, Clone)]
pub struct ArgWrap<'a> {
pub(crate) arg: Arg<'a>,
}

impl<'a> ArgWrap<'a> {
pub fn new(arg: Arg<'a>) -> Self {
Self { arg }
}
}

impl<'a> From<ArgWrap<'a>> for Arg<'a> {
fn from(arg: ArgWrap<'a>) -> Self {
arg.arg
}
}

impl<'a> From<Arg<'a>> for ArgWrap<'a> {
fn from(arg: Arg<'a>) -> Self {
ArgWrap { arg }
}
}

impl<'a> Deref for ArgWrap<'a> {
type Target = Arg<'a>;

fn deref(&self) -> &Self::Target {
&self.arg
}
}

#[cfg(feature = "override-arg")]
struct ArgKVO<'a>(Option<Command<'a>>);

Expand Down Expand Up @@ -121,6 +152,7 @@ impl<'a> Visitor<'a> for ArgVisitor<'a> {
(default_value, &str),
// default_value_if : tuple3
ref (default_value_ifs, Vec<(&str, Option<&str>, Option<&str>)> ),
ref (default_values, Vec<&str> ),
(display_order, usize),
// env : specialized
// env_os // not supported yet
Expand Down Expand Up @@ -240,10 +272,11 @@ impl<'a> Visitor<'a> for ArgVisitor<'a> {
"value_parser" => {
arg.value_parser(map.next_value::<ValueParser>()?)
}
"positional" => {map.next_value::<bool>()?;/* noop */ arg}
]
);
}
Ok(ArgWrap { arg })
Ok(ArgWrap::new(arg))
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/de/arg/value_hint.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use clap::ValueHint as VH;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

enum_de!(VH,ValueHint,
#[derive(Deserialize, Clone, Copy)]
#[derive(Serialize, Deserialize, Clone, Copy)]
#[cfg_attr(feature = "kebab-case-key" ,serde(rename_all = "kebab-case"))]
#[cfg_attr(feature = "snake-case-key" ,serde(rename_all = "snake_case"))]
{
Expand Down
17 changes: 16 additions & 1 deletion src/de/group.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
use crate::ArgGroupWrap;
use clap::{ArgGroup, Command};
use serde::de::{DeserializeSeed, Error, Visitor};

pub(crate) struct ArgGroupWrap<'a> {
group: ArgGroup<'a>,
}

impl<'a> From<ArgGroupWrap<'a>> for ArgGroup<'a> {
fn from(group: ArgGroupWrap<'a>) -> Self {
group.group
}
}

impl<'a> From<ArgGroup<'a>> for ArgGroupWrap<'a> {
fn from(group: ArgGroup<'a>) -> Self {
ArgGroupWrap { group }
}
}

struct GroupVisitor<'a>(&'a str);

impl<'de> Visitor<'de> for GroupVisitor<'de> {
Expand Down
13 changes: 13 additions & 0 deletions src/de/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,18 @@ macro_rules! enum_de {
}
}
}

impl $newty {
#[allow(dead_code)]
pub fn from_clap_type(c : $basety) -> Self {
match c {
$(
$(#[$cfg_meta])*
$basety::$var => $newty::$var,
)*
_ => unimplemented!(),
}
}
}
};
}
6 changes: 3 additions & 3 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[macro_use]
mod macros;

mod app;
mod arg;
mod group;
pub(crate) mod app;
pub(crate) mod arg;
pub(crate) mod group;
128 changes: 14 additions & 114 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]

use clap::{Arg, ArgGroup, Command};
use serde::Deserializer;
use std::ops::Deref;

#[cfg(not(any(
feature = "kebab-case-key",
feature = "snake-case-key",
Expand All @@ -19,125 +15,29 @@ compile_error!("Case setting feature is missing. Either one should be set.");
))]
compile_error!("Case setting feature is conflicting. Only one should be set.");

#[macro_use]
mod de;
pub(crate) mod de;
mod ser;

#[cfg(feature = "docsrs")]
pub mod documents;

#[cfg(feature = "yaml")]
#[deprecated(since = "0.4", note = "use serde-yaml instead")]
mod yaml;

#[cfg(all(test, feature = "snake-case-key"))]
mod tests;

#[cfg(feature = "yaml")]
pub use yaml::{yaml_to_app, YamlWrap};

/**
Deserialize [`Command`] from [`Deserializer`].
```
const CLAP_TOML: &'static str = r#"
name = "app_clap_serde"
version = "1.0"
author = "tester"
about = "test-clap-serde"
"#;
let app = clap_serde::load(&mut toml::Deserializer::new(CLAP_TOML))
.expect("parse failed");
assert_eq!(app.get_name(), "app_clap_serde");
assert_eq!(app.get_about(), Some("test-clap-serde"));
```
*/
pub fn load<'de, D>(de: D) -> Result<Command<'de>, D::Error>
where
D: Deserializer<'de>,
{
use serde::Deserialize;
CommandWrap::deserialize(de).map(|a| a.into())
}

/**
Wrapper of [`Command`] to deserialize.
```
const CLAP_TOML: &'static str = r#"
name = "app_clap_serde"
version = "1.0"
author = "tester"
about = "test-clap-serde"
"#;
let app: clap::Command = toml::from_str::<clap_serde::CommandWrap>(CLAP_TOML)
.expect("parse failed")
.into();
assert_eq!(app.get_name(), "app_clap_serde");
assert_eq!(app.get_about(), Some("test-clap-serde"));
```
*/
#[derive(Debug, Clone)]
pub struct CommandWrap<'a> {
app: Command<'a>,
}
pub use de::app::CommandWrap;
pub use de::arg::ArgWrap;
pub use ser::app::CommandWrapRef;
pub use ser::arg::ArgWrapRef;
pub use ser::NoSkip;

#[deprecated]
#[deprecated(note = "use CommandWrap instead")]
pub type AppWrap<'a> = CommandWrap<'a>;

impl<'a> From<CommandWrap<'a>> for Command<'a> {
fn from(a: CommandWrap<'a>) -> Self {
a.app
}
}

impl<'a> From<Command<'a>> for CommandWrap<'a> {
fn from(app: Command<'a>) -> Self {
CommandWrap { app }
}
}

impl<'a> Deref for CommandWrap<'a> {
type Target = Command<'a>;

fn deref(&self) -> &Self::Target {
&self.app
}
}

/// Wrapper of [`Arg`] to deserialize with [`DeserializeSeed`](`serde::de::DeserializeSeed`).
#[derive(Debug, Clone)]
pub struct ArgWrap<'a> {
arg: Arg<'a>,
}

impl<'a> From<ArgWrap<'a>> for Arg<'a> {
fn from(arg: ArgWrap<'a>) -> Self {
arg.arg
}
}

impl<'a> From<Arg<'a>> for ArgWrap<'a> {
fn from(arg: Arg<'a>) -> Self {
ArgWrap { arg }
}
}

impl<'a> Deref for ArgWrap<'a> {
type Target = Arg<'a>;

fn deref(&self) -> &Self::Target {
&self.arg
}
}

pub(crate) struct ArgGroupWrap<'a> {
group: ArgGroup<'a>,
}

impl<'a> From<ArgGroupWrap<'a>> for ArgGroup<'a> {
fn from(group: ArgGroupWrap<'a>) -> Self {
group.group
}
}
#[cfg(all(test, feature = "snake-case-key"))]
mod tests;

impl<'a> From<ArgGroup<'a>> for ArgGroupWrap<'a> {
fn from(group: ArgGroup<'a>) -> Self {
ArgGroupWrap { group }
}
}
mod util;
pub use util::load;
Loading