nu_protocol/
deprecation.rs1use crate::{FromValue, ParseWarning, ShellError, Type, Value, ast::Call};
2
3use crate::{self as nu_protocol, ReportMode, Span};
7
8#[derive(FromValue)]
15pub struct DeprecationEntry {
16 #[nu_value(rename = "flag", default)]
19 pub ty: DeprecationType,
20 #[nu_value(rename = "report")]
22 pub report_mode: ReportMode,
23 pub since: Option<String>,
25 pub expected_removal: Option<String>,
27 pub help: Option<String>,
29}
30
31#[derive(Default)]
33pub enum DeprecationType {
34 #[default]
36 Command,
37 Flag(String),
39}
40
41impl FromValue for DeprecationType {
42 fn from_value(v: Value) -> Result<Self, ShellError> {
43 match v {
44 Value::String { val, .. } => Ok(DeprecationType::Flag(val)),
45 Value::Nothing { .. } => Ok(DeprecationType::Command),
46 v => Err(ShellError::CantConvert {
47 to_type: Self::expected_type().to_string(),
48 from_type: v.get_type().to_string(),
49 span: v.span(),
50 help: None,
51 }),
52 }
53 }
54
55 fn expected_type() -> Type {
56 Type::String
57 }
58}
59
60impl FromValue for ReportMode {
61 fn from_value(v: Value) -> Result<Self, ShellError> {
62 let span = v.span();
63 let Value::String { val, .. } = v else {
64 return Err(ShellError::CantConvert {
65 to_type: Self::expected_type().to_string(),
66 from_type: v.get_type().to_string(),
67 span: v.span(),
68 help: None,
69 });
70 };
71 match val.as_str() {
72 "first" => Ok(ReportMode::FirstUse),
73 "every" => Ok(ReportMode::EveryUse),
74 _ => Err(ShellError::InvalidValue {
75 valid: "first or every".into(),
76 actual: val,
77 span,
78 }),
79 }
80 }
81
82 fn expected_type() -> Type {
83 Type::String
84 }
85}
86
87impl DeprecationEntry {
88 fn check(&self, call: &Call) -> bool {
89 match &self.ty {
90 DeprecationType::Command => true,
91 DeprecationType::Flag(flag) => {
92 debug_assert!(
94 !flag.starts_with('-'),
95 "DeprecationEntry for {flag} should not include dashes in the flag name!"
96 );
97
98 call.get_named_arg(flag).is_some()
99 }
100 }
101 }
102
103 fn type_name(&self) -> String {
104 match &self.ty {
105 DeprecationType::Command => "Command".to_string(),
106 DeprecationType::Flag(_) => "Flag".to_string(),
107 }
108 }
109
110 fn label(&self, command_name: &str) -> String {
111 let name = match &self.ty {
112 DeprecationType::Command => command_name,
113 DeprecationType::Flag(flag) => &format!("{command_name} --{flag}"),
114 };
115 let since = match &self.since {
116 Some(since) => format!("was deprecated in {since}"),
117 None => "is deprecated".to_string(),
118 };
119 let removal = match &self.expected_removal {
120 Some(expected) => format!("and will be removed in {expected}"),
121 None => "and will be removed in a future release".to_string(),
122 };
123 format!("{name} {since} {removal}.")
124 }
125
126 fn span(&self, call: &Call) -> Span {
127 match &self.ty {
128 DeprecationType::Command => call.span(),
129 DeprecationType::Flag(flag) => call
130 .get_named_arg(flag)
131 .map(|arg| arg.span)
132 .unwrap_or(Span::unknown()),
133 }
134 }
135
136 pub fn parse_warning(self, command_name: &str, call: &Call) -> Option<ParseWarning> {
137 if !self.check(call) {
138 return None;
139 }
140
141 let dep_type = self.type_name();
142 let label = self.label(command_name);
143 let span = self.span(call);
144 let report_mode = self.report_mode;
145 Some(ParseWarning::Deprecated {
146 dep_type,
147 label,
148 span,
149 report_mode,
150 help: self.help,
151 })
152 }
153}