evobench_tools/run/
custom_parameter.rs

1use std::{ffi::OsStr, num::NonZeroU32, str::FromStr};
2
3use anyhow::{Result, anyhow, bail};
4use kstring::KString;
5
6use crate::{
7    run::env_vars::AllowableCustomEnvVar,
8    serde_types::{
9        allowed_env_var::AllowEnvVar, proper_dirname::ProperDirname,
10        proper_filename::ProperFilename,
11    },
12};
13
14/// The value type of a custom parameter--those values are passed as
15/// environment variables and hence as strings, but they are parsed
16/// when read from the user (config) to ensure correctness.
17#[derive(
18    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
19)]
20pub enum CustomParameterType {
21    String,
22    Filename,
23    Dirname,
24    Bool,
25    NonZeroU32,
26    U32,
27}
28
29#[derive(Debug, serde::Serialize, serde::Deserialize)]
30#[serde(deny_unknown_fields)]
31pub struct AllowedCustomParameter {
32    pub required: bool,
33    pub r#type: CustomParameterType,
34}
35
36/// A checked value
37#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
38pub struct CustomParameterValue {
39    r#type: CustomParameterType,
40    value: KString,
41}
42
43impl CustomParameterValue {
44    /// Make this so that "{key}={val}" fits in a path segment, so
45    /// that RunParameters.extend_path() always works.
46    pub const MAX_VALUE_STRING_LEN: usize = 255 - 1 - AllowableCustomEnvVar::MAX_ENV_VAR_NAME_LEN;
47
48    pub fn as_str(&self) -> &str {
49        &self.value
50    }
51
52    pub fn r#type(&self) -> CustomParameterType {
53        self.r#type
54    }
55
56    pub fn checked_from(r#type: CustomParameterType, value: &KString) -> Result<Self> {
57        // Check:
58        match r#type {
59            CustomParameterType::String => {
60                // This check is required, right? What does Rust do
61                // when setting env vars or converting to OsStr? Cut
62                // off?
63                if value.contains('\0') {
64                    bail!("custom parameter values may not contain null characters")
65                }
66            }
67            CustomParameterType::Filename => {
68                let _ = ProperFilename::from_str(value).map_err(|e| anyhow!("expecting {e:#}"))?;
69            }
70            CustomParameterType::Dirname => {
71                let _ = ProperDirname::from_str(value).map_err(|e| anyhow!("expecting {e:#}"))?;
72            }
73            CustomParameterType::Bool => match value.as_str() {
74                "0" | "1" => (),
75                _ => bail!(
76                    "string not valid as a boolean custom parameter, \
77                     expecting \"0\" or \"1\": {:?}",
78                    value.as_str()
79                ),
80            },
81            CustomParameterType::NonZeroU32 => {
82                let _ = NonZeroU32::from_str(value)?;
83            }
84            CustomParameterType::U32 => {
85                let _ = u32::from_str(value)?;
86            }
87        }
88        // OK.
89        Ok(Self {
90            r#type,
91            value: value.clone(),
92        })
93    }
94}
95
96impl AsRef<OsStr> for CustomParameterValue {
97    fn as_ref(&self) -> &OsStr {
98        self.as_str().as_ref()
99    }
100}
101
102impl AsRef<str> for CustomParameterValue {
103    fn as_ref(&self) -> &str {
104        self.as_str().as_ref()
105    }
106}