1use std::collections::BTreeMap;
4use std::convert::Infallible;
5use std::fmt;
6use std::path::PathBuf;
7use std::str::FromStr;
8
9pub type FolderId = usize;
11
12pub type SshOptionMap = BTreeMap<String, Vec<String>>;
14
15#[derive(Debug, Clone, PartialEq, Eq, Default)]
17pub enum ConnectionProtocol {
18 #[default]
20 Ssh,
21 Rdp,
23 Other(String),
25}
26
27impl ConnectionProtocol {
28 fn parse(value: &str) -> Self {
29 match value.trim().to_ascii_lowercase().as_str() {
30 "" | "ssh" => Self::Ssh,
31 "rdp" => Self::Rdp,
32 other => Self::Other(other.to_string()),
33 }
34 }
35
36 pub fn as_str(&self) -> &str {
37 match self {
38 Self::Ssh => "ssh",
39 Self::Rdp => "rdp",
40 Self::Other(value) => value.as_str(),
41 }
42 }
43
44 pub fn display_name(&self) -> &str {
45 match self {
46 Self::Ssh => "SSH",
47 Self::Rdp => "RDP",
48 Self::Other(value) => value.as_str(),
49 }
50 }
51}
52
53impl From<&str> for ConnectionProtocol {
54 fn from(value: &str) -> Self {
55 Self::parse(value)
56 }
57}
58
59impl FromStr for ConnectionProtocol {
60 type Err = Infallible;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 Ok(Self::parse(s))
64 }
65}
66
67impl fmt::Display for ConnectionProtocol {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.write_str(self.as_str())
70 }
71}
72
73#[derive(Debug, Clone, Default)]
75pub struct SshHostOptions {
76 pub identity_files: Vec<String>,
78 pub identities_only: Option<bool>,
80 pub proxy_jump: Option<String>,
82 pub proxy_command: Option<String>,
84 pub forward_agent: Option<String>,
86 pub local_forward: Vec<String>,
88 pub remote_forward: Vec<String>,
90 pub extra_options: SshOptionMap,
92}
93
94#[derive(Debug, Clone, Default)]
96pub struct RdpHostOptions {
97 pub domain: Option<String>,
99 pub args: Vec<String>,
101}
102
103#[derive(Debug, Clone)]
105pub struct InventoryHost {
106 pub name: String,
108 pub description: Option<String>,
110 pub protocol: ConnectionProtocol,
112 pub host: String,
114 pub user: Option<String>,
116 pub port: Option<u16>,
118 pub profile: Option<String>,
120 pub vault_pass: Option<String>,
122 pub hidden: bool,
124 pub ssh: SshHostOptions,
126 pub rdp: RdpHostOptions,
128 pub source_file: PathBuf,
130 pub source_folder_path: Vec<String>,
132}
133
134impl InventoryHost {
135 pub fn new(name: String) -> Self {
137 Self {
138 host: name.clone(),
139 name,
140 description: None,
141 protocol: ConnectionProtocol::Ssh,
142 user: None,
143 port: None,
144 profile: None,
145 vault_pass: None,
146 hidden: false,
147 ssh: SshHostOptions::default(),
148 rdp: RdpHostOptions::default(),
149 source_file: PathBuf::new(),
150 source_folder_path: Vec::new(),
151 }
152 }
153}
154
155#[derive(Debug, Clone)]
157pub struct TreeFolder {
158 pub id: FolderId,
160 pub name: String,
162 pub path: PathBuf,
164 pub children: Vec<TreeFolder>,
166 pub host_indices: Vec<usize>,
168}
169
170#[derive(Debug, Clone)]
172pub struct InventoryTreeModel {
173 pub root: TreeFolder,
175 pub hosts: Vec<InventoryHost>,
177}
178
179impl InventoryTreeModel {
180 pub(super) fn empty(root_path: PathBuf) -> Self {
181 let root_name = root_path
182 .file_name()
183 .and_then(|segment| segment.to_str())
184 .unwrap_or("cossh-inventory.yaml")
185 .to_string();
186 Self {
187 root: TreeFolder {
188 id: 0,
189 name: root_name,
190 path: root_path,
191 children: Vec::new(),
192 host_indices: Vec::new(),
193 },
194 hosts: Vec::new(),
195 }
196 }
197}
198
199#[derive(Debug, Clone, Default)]
200pub(super) struct ParsedInventoryDocument {
201 pub include: Vec<String>,
202 pub inventory: Vec<InventoryNodeRaw>,
203}
204
205#[derive(Debug, Clone)]
206pub(super) enum InventoryNodeRaw {
207 Host(Box<InventoryHostRaw>),
208 Folder { name: String, items: Vec<InventoryNodeRaw> },
209}
210
211#[derive(Debug, Clone, Default)]
212pub(super) struct InventoryHostRaw {
213 pub name: String,
214 pub description: Option<String>,
215 pub protocol: ConnectionProtocol,
216 pub host: Option<String>,
217 pub user: Option<String>,
218 pub port: Option<u16>,
219 pub profile: Option<String>,
220 pub vault_pass: Option<String>,
221 pub hidden: bool,
222 pub identity_files: Vec<String>,
223 pub identities_only: Option<bool>,
224 pub proxy_jump: Option<String>,
225 pub proxy_command: Option<String>,
226 pub forward_agent: Option<String>,
227 pub local_forward: Vec<String>,
228 pub remote_forward: Vec<String>,
229 pub ssh_options: SshOptionMap,
230 pub rdp_domain: Option<String>,
231 pub rdp_args: Vec<String>,
232}
233
234#[cfg(test)]
235#[path = "../test/inventory/model.rs"]
236mod tests;