1use serde::{Deserialize, Serialize};
6use std::path::{Path, PathBuf};
7use strop_workspace::{AddressError, RemoteLocation};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum FileTarget {
13 Local(PathBuf),
14 Remote(RemoteLocation),
15 Container {
18 container: strop_workspace::ContainerId,
19 path: PathBuf,
20 },
21}
22
23impl Serialize for FileTarget {
24 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
25 #[derive(Serialize)]
26 struct RemoteRecord<'a> {
27 remote: &'a RemoteLocation,
28 }
29 #[derive(Serialize)]
30 struct ContainerRecord<'a> {
31 container: &'a strop_workspace::ContainerId,
32 #[serde(with = "strop_core::path_serde")]
33 path: &'a PathBuf,
34 }
35 match self {
36 Self::Local(path) => strop_core::path_serde::serialize(path, serializer),
37 Self::Remote(remote) => RemoteRecord { remote }.serialize(serializer),
38 Self::Container { container, path } => {
39 ContainerRecord { container, path }.serialize(serializer)
40 }
41 }
42 }
43}
44impl<'de> Deserialize<'de> for FileTarget {
45 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
46 #[derive(Deserialize)]
47 #[serde(untagged)]
48 enum Record {
49 Remote {
50 remote: RemoteLocation,
51 },
52 Container {
53 container: strop_workspace::ContainerId,
54 #[serde(with = "strop_core::path_serde")]
55 path: PathBuf,
56 },
57 Local(#[serde(with = "strop_core::path_serde")] PathBuf),
58 }
59 Ok(match Record::deserialize(deserializer)? {
60 Record::Remote { remote } => Self::Remote(remote),
61 Record::Container { container, path } => Self::Container { container, path },
62 Record::Local(path) => Self::Local(path),
63 })
64 }
65}
66impl FileTarget {
67 pub fn parse(value: PathBuf) -> Result<Self, AddressError> {
70 match value.to_str().filter(|text| text.starts_with("ssh://")) {
71 Some(uri) => RemoteLocation::parse(uri).map(Self::Remote),
72 None => Ok(Self::Local(value)),
73 }
74 }
75 pub fn local_path(&self) -> Option<&Path> {
76 match self {
77 Self::Local(path) => Some(path),
78 _ => None,
79 }
80 }
81}
82impl std::fmt::Display for FileTarget {
83 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 match self {
85 Self::Local(path) => path.display().fmt(formatter),
86 Self::Remote(file) => file.fmt(formatter),
87 Self::Container { container, path } => write!(
88 formatter,
89 "container:{}{}",
90 &container.as_str()[..12],
91 path.display()
92 ),
93 }
94 }
95}