-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.rs
More file actions
146 lines (122 loc) · 3.87 KB
/
Copy pathcli.rs
File metadata and controls
146 lines (122 loc) · 3.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use crate::{
error::Result,
source::{ConfigSource, Source},
};
use clap::Parser;
use serde_json::Value;
use std::any::Any;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Cli {
parsed_values: HashMap<String, Value>,
field_mappings: HashMap<String, String>,
}
impl Cli {
pub fn from_args() -> Self {
Self::from_vec(std::env::args().collect())
}
pub fn from_vec(args: Vec<String>) -> Self {
let mut parsed_values = HashMap::new();
let mut i = 1;
while i < args.len() {
let arg = &args[i];
if arg.starts_with("--") {
let key = arg.trim_start_matches("--");
if i + 1 < args.len() && !args[i + 1].starts_with("--") {
let value = &args[i + 1];
parsed_values.insert(key.to_string(), Self::parse_value(value));
i += 2;
} else {
parsed_values.insert(key.to_string(), Value::Bool(true));
i += 1;
}
} else if arg.starts_with("-") && arg.len() == 2 {
let key = arg.trim_start_matches("-");
if i + 1 < args.len() && !args[i + 1].starts_with("-") {
let value = &args[i + 1];
parsed_values.insert(key.to_string(), Self::parse_value(value));
i += 2;
} else {
parsed_values.insert(key.to_string(), Value::Bool(true));
i += 1;
}
} else {
i += 1;
}
}
Self {
parsed_values,
field_mappings: HashMap::new(),
}
}
pub fn with_clap_app<T: Parser + serde::Serialize>() -> Result<Self> {
let app = T::parse();
let json_value = serde_json::to_value(&app).map_err(|e| {
crate::error::Error::Serialization(format!("Failed to serialize clap args: {e}"))
})?;
let mut parsed_values = HashMap::new();
if let Value::Object(map) = json_value {
for (key, value) in map {
parsed_values.insert(key, value);
}
}
Ok(Self {
parsed_values,
field_mappings: HashMap::new(),
})
}
pub fn with_field_mapping(
mut self,
field_name: impl Into<String>,
cli_key: impl Into<String>,
) -> Self {
self.field_mappings
.insert(field_name.into(), cli_key.into());
self
}
fn parse_value(value: &str) -> Value {
if let Ok(b) = value.parse::<bool>() {
return Value::Bool(b);
}
if let Ok(n) = value.parse::<i64>() {
return Value::Number(n.into());
}
if let Ok(n) = value.parse::<f64>() {
// Handle NaN and infinite values safely
if let Some(num) = serde_json::Number::from_f64(n) {
return Value::Number(num);
}
}
if value.starts_with('[') && value.ends_with(']') {
if let Ok(arr) = serde_json::from_str::<Vec<Value>>(value) {
return Value::Array(arr);
}
}
Value::String(value.to_string())
}
pub fn get_matches(&self) -> &HashMap<String, Value> {
&self.parsed_values
}
}
impl ConfigSource for Cli {
fn source_type(&self) -> Source {
Source::Cli
}
fn collect(&self) -> Result<Value> {
Ok(Value::Object(
self.parsed_values
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
))
}
fn has_value(&self, key: &str) -> bool {
self.parsed_values.contains_key(key)
}
fn get_value(&self, key: &str) -> Option<Value> {
self.parsed_values.get(key).cloned()
}
fn as_any(&self) -> &dyn Any {
self
}
}