arcs_ctf_yaml_parser/flag/
mod.rs

1use std::{fmt::{Display, Debug}, path::{PathBuf, Path}, str::FromStr, io::ErrorKind};
2
3use serde_yaml::Value as YamlValue;
4
5use crate::structs::{get_type, ValueType};
6
7
8pub fn get_file_flag(path: PathBuf, base_path: &Path) -> Result<Flag, FlagError> {
9    match std::fs::read_to_string(base_path.join(&path)) {
10        Ok(s) => Ok(Flag::File(path, s)),
11        Err(e) => if e.kind() == ErrorKind::NotFound {
12            Err(FlagError::FileMissing(path))
13        } else {
14            Err(FlagError::OsError(path))
15        },
16    }
17}
18
19pub fn get_flag(value: &YamlValue, base_path: &Path) -> Result<Flag, FlagError> {
20    if let Some(flag_str) = value.as_str() {
21        Ok(Flag::String(flag_str.trim().to_string()))
22    } else if let Some(mapping) = value.as_mapping() {
23        if let Some(Some(file)) = mapping.get("file").map(YamlValue::as_str) {
24            if let Ok(path) = PathBuf::from_str(file) {
25                if path.is_relative() {
26                    get_file_flag(path, base_path)
27                } else {
28                    Err(FlagError::BadPath(file.to_string()))
29                }
30            } else {
31                Err(FlagError::BadPath(file.to_string()))
32            }
33        } else {
34            Err(FlagError::MappingNeedsFile)
35        }
36    } else {
37        Err(FlagError::BadType(get_type(value)))
38    }
39}
40
41#[derive(Clone, PartialEq)]
42pub enum Flag {
43    String(String),
44    File(PathBuf, String),
45}
46impl Flag {
47    pub fn as_str(&self) -> &str {
48        match self {
49            Self::String(s) | Self::File(_, s) => s.trim(),
50        }
51    }
52    pub fn path(&self) -> Option<&std::path::Path> {
53        if let Self::File(p, _) = self {
54            Some(p.as_path())
55        } else { None }
56    }
57}
58
59impl Debug for Flag {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        use Flag::{ String, File };
62        match self {
63            String(s) => write!(f, "Flag< {s} >"),
64            File(p, s) => write!(f, "Flag< {s} (@ {}) >", p.display()),
65        }
66    }
67}
68
69
70#[derive(Default, Debug, Clone)]
71pub enum FlagError {
72    BadType(ValueType),
73    
74    BadString(String),
75
76    BadPath(String),
77    MappingNeedsFile,
78    FileMissing(PathBuf),
79    OsError(PathBuf),
80    
81    #[default]
82    MissingKey,
83}
84
85impl Display for FlagError {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        use FlagError::*;
88        match self {
89            BadType(t) => write!(f, "Flag should be a list, not {t}."),
90            BadString(s) => write!(f, "The string {s} is not a valid flag."),
91            BadPath(p) => write!(f, "The string {p} is not a valid path. (hint: If you want to define a flag with a string, use `flag: <input>`)"),
92            MappingNeedsFile => write!(f, "If you are going to define a flag via a file, you need to have `file: <path>` as an entry under `flag`. (<path> must be a string)"),
93            MissingKey => write!(f, "You have to define `categories`."),
94            FileMissing(p) => write!(f, "There is no file at {}.", p.display()),
95            OsError(p) => write!(f, "There was an issue opening the file at {}. Maybe check permissions?", p.display()),
96        }
97    }
98}