nu_command/strings/str_/case/
capitalize.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct StrCapitalize;
5
6impl Command for StrCapitalize {
7    fn name(&self) -> &str {
8        "str capitalize"
9    }
10
11    fn signature(&self) -> Signature {
12        Signature::build("str capitalize")
13            .input_output_types(vec![
14                (Type::String, Type::String),
15                (
16                    Type::List(Box::new(Type::String)),
17                    Type::List(Box::new(Type::String)),
18                ),
19                (Type::table(), Type::table()),
20                (Type::record(), Type::record()),
21            ])
22            .allow_variants_without_examples(true)
23            .rest(
24                "rest",
25                SyntaxShape::CellPath,
26                "For a data structure input, convert strings at the given cell paths.",
27            )
28            .category(Category::Strings)
29    }
30
31    fn description(&self) -> &str {
32        "Capitalize first letter of text."
33    }
34
35    fn search_terms(&self) -> Vec<&str> {
36        vec!["convert", "style", "caps", "upper"]
37    }
38
39    fn is_const(&self) -> bool {
40        true
41    }
42
43    fn run(
44        &self,
45        engine_state: &EngineState,
46        stack: &mut Stack,
47        call: &Call,
48        input: PipelineData,
49    ) -> Result<PipelineData, ShellError> {
50        let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
51        operate(engine_state, call, input, column_paths)
52    }
53
54    fn run_const(
55        &self,
56        working_set: &StateWorkingSet,
57        call: &Call,
58        input: PipelineData,
59    ) -> Result<PipelineData, ShellError> {
60        let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
61        operate(working_set.permanent(), call, input, column_paths)
62    }
63
64    fn examples(&self) -> Vec<Example<'_>> {
65        vec![
66            Example {
67                description: "Capitalize contents",
68                example: "'good day' | str capitalize",
69                result: Some(Value::test_string("Good day")),
70            },
71            Example {
72                description: "Capitalize contents",
73                example: "'anton' | str capitalize",
74                result: Some(Value::test_string("Anton")),
75            },
76            Example {
77                description: "Capitalize a column in a table",
78                example: "[[lang, gems]; [nu_test, 100]] | str capitalize lang",
79                result: Some(Value::test_list(vec![Value::test_record(record! {
80                    "lang" => Value::test_string("Nu_test"),
81                    "gems" => Value::test_int(100),
82                })])),
83            },
84        ]
85    }
86}
87
88fn operate(
89    engine_state: &EngineState,
90    call: &Call,
91    input: PipelineData,
92    column_paths: Vec<CellPath>,
93) -> Result<PipelineData, ShellError> {
94    let head = call.head;
95    input.map(
96        move |v| {
97            if column_paths.is_empty() {
98                action(&v, head)
99            } else {
100                let mut ret = v;
101                for path in &column_paths {
102                    let r =
103                        ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
104                    if let Err(error) = r {
105                        return Value::error(error, head);
106                    }
107                }
108                ret
109            }
110        },
111        engine_state.signals(),
112    )
113}
114
115fn action(input: &Value, head: Span) -> Value {
116    match input {
117        Value::String { val, .. } => Value::string(uppercase_helper(val), head),
118        Value::Error { .. } => input.clone(),
119        _ => Value::error(
120            ShellError::OnlySupportsThisInputType {
121                exp_input_type: "string".into(),
122                wrong_type: input.get_type().to_string(),
123                dst_span: head,
124                src_span: input.span(),
125            },
126            head,
127        ),
128    }
129}
130
131fn uppercase_helper(s: &str) -> String {
132    // apparently more performant https://stackoverflow.com/questions/38406793/why-is-capitalizing-the-first-letter-of-a-string-so-convoluted-in-rust
133    let mut chars = s.chars();
134    match chars.next() {
135        None => String::new(),
136        Some(f) => f.to_uppercase().collect::<String>() + chars.as_str(),
137    }
138}
139
140#[cfg(test)]
141mod test {
142    use super::*;
143
144    #[test]
145    fn test_examples() {
146        use crate::test_examples;
147
148        test_examples(StrCapitalize {})
149    }
150}