opstr/ops/
format.rs

1use crate::config::Configuration;
2use crate::errors::LibError;
3use crate::input::Args;
4use crate::ops::traits;
5use crate::ops::traits::Op;
6use crate::output::Output;
7use crate::range;
8
9use std::fmt;
10
11use rt_format::ParsedFormat;
12use rt_format::Specifier;
13use rt_format::argument::FormatArgument;
14
15pub struct Format {}
16
17impl Format {
18    fn function_for_chars(args: &[&str]) -> Result<String, LibError> {
19        match args.len() {
20            0 => Err(LibError::ArgumentCountError(Self::acceptable_number_of_arguments(), 0, None)),
21            1 => Ok(args[0].into()),
22            _ => {
23                let template: &str = args[0];
24                if !template.contains("{") {
25                    return Ok(template.to_owned());
26                }
27
28                // prepare arguments
29                let mut fmt_args = vec![];
30                for arg in args.iter().skip(1) {
31                    fmt_args.push(FmtArg(arg.to_string()));
32                }
33
34                let repr = match ParsedFormat::parse(template, &fmt_args, &rt_format::NoNamedArguments) {
35                    Ok(val) => val.to_string(),
36                    Err(failing_pos) => return Err(LibError::ArgValueError(0, format!("format string is invalid at zero-based position {}", failing_pos))),
37                };
38
39                Ok(repr)
40            }
41        }
42    }
43}
44
45impl traits::Op for Format {
46    fn name() -> &'static str { "format" }
47    fn usage() -> &'static str { "<#1 string format-with-placeholders> [<#2 string arg> 0 or more times]" }
48    fn description() -> &'static str { "replace {placeholders} in string #1 with consecutive arguments #2, #3, …" }
49    fn acceptable_number_of_arguments() -> range::Range { range::Range::IndexOpen(1) }
50
51    fn priority(args: &Args, _conf: &Configuration) -> Result<f32, LibError> {
52        let template: &str = args.get(0)?.try_into()?;
53        let occurences_start = template.matches('{').count().max(5);
54        let occurences_end = template.matches('}').count().max(5);
55
56        if occurences_start < (args.len() - 1) {
57            // NOTE: there are not sufficient placeholders for the arguments
58            return Ok(0.0);
59        }
60
61        let mut score = 0.75 + (0.05 * occurences_start as f32);
62        if occurences_start != occurences_end {
63            score *= 0.5;
64        }
65        Ok(score)
66    }
67
68    fn run(args: &Args, _conf: &Configuration) -> Result<Output, LibError> {
69        match args.len() {
70            0 => Ok("".into()),
71            1 => {
72                let arg: &str = args.get(0)?.try_into()?;
73                Ok(arg.into())
74            },
75            _ => {
76                let mut arguments = vec![];
77                for arg in args.iter() {
78                    let s: &str = arg.try_into()?;
79                    arguments.push(s);
80                }
81        
82                Ok(Self::function_for_chars(&arguments)?.into())
83            }
84        }
85    }
86}
87
88#[derive(Debug)]
89pub struct FmtArg(String);
90
91impl<'s> FormatArgument for FmtArg {
92    fn supports_format(&self, _specifier: &Specifier) -> bool { true }
93
94    // NOTE do not use write! instead of fmt(…) here,
95    //      as this would ignore the specifier
96
97    fn fmt_display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        fmt::Display::fmt(&self.0, f)
99    }
100
101    fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        fmt::Debug::fmt(&self.0, f)
103    }
104
105    fn fmt_octal(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        match self.0.parse::<i64>() {
107            Ok(int) => fmt::Display::fmt(&int, f),
108            Err(_) => Err(std::fmt::Error),
109        }
110    }
111
112    fn fmt_lower_hex(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        match self.0.parse::<i64>() {
114            Ok(int) => fmt::Display::fmt(&int, f),
115            Err(_) => Err(std::fmt::Error),
116        }
117    }
118
119    fn fmt_upper_hex(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self.0.parse::<i64>() {
121            Ok(int) => fmt::Display::fmt(&int, f),
122            Err(_) => Err(std::fmt::Error),
123        }
124    }
125
126    fn fmt_binary(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        match self.0.parse::<i64>() {
128            Ok(int) => fmt::Display::fmt(&int, f),
129            Err(_) => Err(std::fmt::Error),
130        }
131    }
132
133    fn fmt_lower_exp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self.0.parse::<i64>() {
135            Ok(int) => fmt::Display::fmt(&int, f),
136            Err(_) => Err(std::fmt::Error),
137        }
138    }
139
140    fn fmt_upper_exp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self.0.parse::<i64>() {
142            Ok(int) => fmt::Display::fmt(&int, f),
143            Err(_) => Err(std::fmt::Error),
144        }
145    }
146}