1use std::{
2 fs,
3 path::{Path, PathBuf},
4 str::FromStr,
5};
6
7use anyhow::{Context, Result};
8use serde::Deserialize;
9
10#[derive(Debug, Clone, Deserialize)]
11#[serde(from = "String")]
12pub enum StringOrFile {
13 String(String),
14 File(PathBuf),
15}
16
17impl StringOrFile {
18 pub fn resolve(&self) -> Result<Vec<u8>> {
19 match self {
20 StringOrFile::String(s) => Ok(s.as_bytes().to_vec()),
21 StringOrFile::File(path) => {
22 fs::read(path).with_context(|| format!("Failed to read file: {}", path.display()))
23 }
24 }
25 }
26
27 pub fn as_str(&self) -> Option<&str> {
28 match self {
29 StringOrFile::String(s) => Some(s),
30 StringOrFile::File(_) => None,
31 }
32 }
33
34 pub fn as_path(&self) -> Option<&Path> {
35 match self {
36 StringOrFile::String(_) => None,
37 StringOrFile::File(path) => Some(path),
38 }
39 }
40}
41
42impl FromStr for StringOrFile {
43 type Err = std::convert::Infallible;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 if let Some(path) = s.strip_prefix('@') {
47 Ok(StringOrFile::File(PathBuf::from(path)))
48 } else {
49 Ok(StringOrFile::String(s.to_string()))
50 }
51 }
52}
53
54impl From<String> for StringOrFile {
55 fn from(s: String) -> Self {
56 StringOrFile::from_str(&s).unwrap()
57 }
58}
59
60#[derive(Debug, Clone, Deserialize)]
61#[serde(from = "String")]
62pub struct FormField {
63 pub name: String,
64 pub value: StringOrFile,
65}
66
67impl FromStr for FormField {
68 type Err = String;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 let (name, value) = s
72 .split_once('=')
73 .ok_or_else(|| format!("Invalid form field format: '{}'. Expected 'key=value'", s))?;
74
75 let value = StringOrFile::from_str(value)
76 .map_err(|e| format!("Failed to parse form field value: {}", e))?;
77
78 Ok(FormField {
79 name: name.to_string(),
80 value,
81 })
82 }
83}
84
85impl From<String> for FormField {
86 fn from(s: String) -> Self {
87 FormField::from_str(&s).unwrap()
88 }
89}