arcs_ctf_yaml_parser/deploy/
mod.rs

1pub mod error;
2pub mod structs;
3
4
5
6use serde_yaml::Value as YamlValue;
7use std::path::PathBuf;
8use std::str::FromStr;
9
10use crate::structs::get_type;
11use crate::Flop;
12
13use self::{
14    error::{DeployOptionsError, DeploymentTargetOptionsError, ExposeError, BuildError},
15    structs::{DeployOptions, DeployTarget, NetworkProtocol},
16};
17
18
19const DEFAULT_REPLICAS: u8 = 1;
20
21pub fn parse_expose(expose: &str) -> Result<NetworkProtocol, ExposeError> {
22
23    let (port, protocol) = expose
24        .split_once('/')
25        .ok_or_else(|| ExposeError::BadFormat(expose.to_string()))?;
26
27    let (port, protocol_is_tcp) = (
28        port.parse::<u32>(),
29        match protocol {
30            "udp" => Ok(false),
31            "tcp" => Ok(true),
32            _ => Err(()),
33        },
34    );
35
36    match (port, protocol_is_tcp) {
37        (Ok(port), Ok(protocol_is_tcp)) => Ok(if protocol_is_tcp {
38            NetworkProtocol::Tcp(port)
39        } else {
40            NetworkProtocol::Udp(port)
41        }),
42        (port, protocol_is_tcp) => Err(ExposeError::BadParts {
43            data: expose.to_string(),
44            port: port.is_err(),
45            protocol: protocol_is_tcp.is_err(),
46        }),
47    }
48}
49
50pub fn parse_deploy_target(value: &YamlValue) -> Result<DeployTarget, DeploymentTargetOptionsError> {
51    let mapping = value.as_mapping().ok_or_else(|| DeploymentTargetOptionsError::BadBaseType(get_type(value)))?;
52
53
54    let expose = mapping
55        .get("expose")
56        .and_then(YamlValue::as_str)
57        .map_or(Err(ExposeError::Missing), parse_expose);
58
59
60    let build = 'path_block: {
61        let Some(value) = mapping.get("src") else {
62            break 'path_block Ok(PathBuf::from("."))
63        };
64        
65        let Some(string) = value.as_str() else {
66            break 'path_block Err(BuildError::BadType(get_type(value)))
67        };
68
69        let Ok(path) = PathBuf::from_str(string) else {
70            break 'path_block Err(BuildError::NotPath(string.to_string()))
71        };
72    
73        if !path.is_relative() {
74            break 'path_block Err(BuildError::NotRelative(path));
75        }
76
77        Ok(path)
78    };
79
80    let replicas = mapping.get("replicas").map(|replicas_val| {
81        replicas_val
82            .as_u64()
83            .map(u8::try_from)
84            .and_then(Result::ok)
85            .ok_or_else(|| get_type(replicas_val))
86    }).flop();
87
88    match (expose, replicas, build) {
89        (Ok(expose), Ok(replicas), Ok(build)) => Ok(DeployTarget {
90            expose,
91            replicas: replicas.unwrap_or(DEFAULT_REPLICAS),
92            build,
93        }),
94        (expose, replicas, build) => Err(DeploymentTargetOptionsError::Parts {
95            expose: expose.err(),
96            replicas_invalid: replicas.err(),
97            build: build.err(),
98        })
99    }
100
101}
102
103pub fn parse_deploy(value: &YamlValue) -> Result<DeployOptions, DeployOptionsError> {
104    let mapping = value.as_mapping().ok_or_else(|| DeployOptionsError::BadBaseType(get_type(value)))?;
105
106    let web = mapping
107        .get("web")
108        .map(parse_deploy_target)
109        .flop();
110
111    let admin = mapping
112        .get("admin")
113        .map(parse_deploy_target)
114        .flop();
115
116    let nc = mapping
117        .get("nc")
118        .map(parse_deploy_target)
119        .flop();
120
121    match (web, admin, nc) {
122        (Ok(web), Ok(admin), Ok(nc)) => Ok(DeployOptions { web, admin, nc }),
123        (web, admin, nc) => Err(DeployOptionsError::Parts {
124            web: Box::new(web.err()),
125            admin: Box::new(admin.err()),
126            nc: Box::new(nc.err()),
127        })
128    }
129}
130