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