1use std::path::{Path, PathBuf};
2
3use serde::{de::Error, Deserialize, Serialize};
4use strum_macros::{Display, EnumIter};
5
6use crate::{
7 enums::{unit_enum_deserialize, unit_enum_from_str},
8 util::normalize_path,
9};
10
11#[derive(Debug, Serialize)]
12pub struct Shell {
13 program: PathBuf,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 kind: Option<ShellKind>,
16}
17
18impl Shell {
19 pub fn new(program: &Path, kind: Option<ShellKind>) -> Self {
20 Self {
21 program: program.to_owned(),
22 kind,
23 }
24 }
25
26 pub fn program(&self) -> &Path {
27 &self.program
28 }
29
30 pub fn kind(&self) -> Option<ShellKind> {
31 self.kind
32 }
33}
34
35impl std::fmt::Display for Shell {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 f.write_str(&self.program.display().to_string())
38 }
39}
40
41impl<'de> Deserialize<'de> for Shell {
42 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43 where
44 D: serde::Deserializer<'de>,
45 {
46 #[derive(Deserialize)]
47 #[serde(remote = "Shell")]
48 struct ShellObject {
49 program: PathBuf,
50 kind: Option<ShellKind>,
51 }
52
53 #[derive(Deserialize)]
54 #[serde(untagged)]
55 enum ShellDeserializationMode {
56 Path(PathBuf),
57 #[serde(with = "ShellObject")]
58 Object(Shell),
59 }
60
61 Ok(match ShellDeserializationMode::deserialize(deserializer)? {
62 ShellDeserializationMode::Object(mut shell) => {
63 shell.program = normalize_path(&shell.program).map_err(D::Error::custom)?;
64 shell
65 }
66 ShellDeserializationMode::Path(program) => Shell {
67 program: normalize_path(program).map_err(D::Error::custom)?,
68 kind: None,
69 },
70 })
71 }
72}
73
74#[derive(Debug, Display, Serialize, EnumIter, Clone, Copy)]
75pub enum ShellKind {
76 Posix,
77 Cmd,
78 Powershell,
79}
80
81unit_enum_from_str!(ShellKind);
82unit_enum_deserialize!(ShellKind);