Skip to main content

netconv_core/
traits.rs

1use crate::ir::NetworkConfig;
2use crate::profile::{detect_domain_mismatches, DeviceProfile};
3use crate::report::ConversionReport;
4
5/// Парсер: текст конфига → IR + частичный репорт (что не распознано)
6pub trait ConfigParser {
7    type Error: std::fmt::Debug;
8
9    fn parse(&self, input: &str) -> Result<(NetworkConfig, ConversionReport), Self::Error>;
10
11    fn vendor_name(&self) -> &str;
12}
13
14/// Рендерер: IR → текст конфига целевого вендора + дополняет репорт
15pub trait ConfigRenderer {
16    type Error: std::fmt::Debug;
17
18    fn render(
19        &self,
20        config: &NetworkConfig,
21        report: &mut ConversionReport,
22    ) -> Result<String, Self::Error>;
23
24    fn vendor_name(&self) -> &str;
25}
26
27/// Полный пайплайн: парсер + рендерер → одна функция конвертации
28pub fn convert<P, R>(
29    parser: &P,
30    renderer: &R,
31    input: &str,
32) -> Result<ConversionOutput, ConvertError>
33where
34    P: ConfigParser,
35    R: ConfigRenderer,
36{
37    let (ir, mut report) = parser
38        .parse(input)
39        .map_err(|e| ConvertError::ParseError(format!("{:?}", e)))?;
40
41    let output = renderer
42        .render(&ir, &mut report)
43        .map_err(|e| ConvertError::RenderError(format!("{:?}", e)))?;
44
45    Ok(ConversionOutput {
46        config_text: output,
47        report,
48    })
49}
50
51/// Тот же пайплайн что и `convert`, но дополнительно сверяет разобранный
52/// IR с выбранным профилем устройства (L2/L3) и кладёт найденные
53/// несоответствия домена в `report.domain_mismatches` — до того как
54/// рендерер успеет молча отрисовать или тихо отбросить чужой домен.
55///
56/// Рендерер на этом этапе ещё не фильтрует IR по профилю — это следующий
57/// шаг (разделение render-крейтов на l2/l3). Здесь только диагностика.
58pub fn convert_with_profile<P, R>(
59    parser: &P,
60    renderer: &R,
61    input: &str,
62    profile: DeviceProfile,
63) -> Result<ConversionOutput, ConvertError>
64where
65    P: ConfigParser,
66    R: ConfigRenderer,
67{
68    let (ir, mut report) = parser
69        .parse(input)
70        .map_err(|e| ConvertError::ParseError(format!("{:?}", e)))?;
71
72    report.domain_mismatches = detect_domain_mismatches(&ir, profile);
73
74    let output = renderer
75        .render(&ir, &mut report)
76        .map_err(|e| ConvertError::RenderError(format!("{:?}", e)))?;
77
78    Ok(ConversionOutput {
79        config_text: output,
80        report,
81    })
82}
83
84#[derive(Debug)]
85pub struct ConversionOutput {
86    pub config_text: String,
87    pub report: ConversionReport,
88}
89
90#[derive(Debug)]
91pub enum ConvertError {
92    ParseError(String),
93    RenderError(String),
94}
95
96impl std::fmt::Display for ConvertError {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        match self {
99            ConvertError::ParseError(e) => write!(f, "Parse error: {}", e),
100            ConvertError::RenderError(e) => write!(f, "Render error: {}", e),
101        }
102    }
103}