Skip to main content

eggress_pproxy_compat/
warnings.rs

1use std::fmt;
2
3use crate::diagnostics::StructuredDiagnostic;
4use crate::issues::{CompatIssue, IssueSeverity};
5
6/// A warning emitted during pproxy compatibility translation.
7///
8/// Legacy view over [`CompatIssue`]: [`TranslationOutput`] stores typed
9/// issues and derives these on demand. The `category` tag round-trips
10/// losslessly through the issue model.
11#[derive(Debug, Clone, PartialEq)]
12pub struct CompatWarning {
13    /// Short category tag (e.g. "unsupported-scheme", "partial-behavior").
14    pub category: &'static str,
15    /// Human-readable message (credentials are redacted).
16    pub message: String,
17}
18
19impl fmt::Display for CompatWarning {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "[{}] {}", self.category, self.message)
22    }
23}
24
25/// An unsupported feature detected during translation.
26///
27/// Legacy view over [`CompatIssue`]: the `feature` tag round-trips
28/// losslessly through the issue model.
29#[derive(Debug, Clone, PartialEq)]
30pub struct UnsupportedFeature {
31    /// Feature name.
32    pub feature: &'static str,
33    /// Details about the input that triggered this.
34    pub detail: String,
35}
36
37impl fmt::Display for UnsupportedFeature {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "unsupported {}: {}", self.feature, self.detail)
40    }
41}
42
43/// Result of translating pproxy-compatible input.
44///
45/// Stores one typed [`CompatIssue`] per finding. The legacy `warnings` /
46/// `unsupported` collections are views (filters) over those issues, and the
47/// human/JSON renderings derive from the same model, so classifications
48/// cannot drift between outputs.
49#[derive(Debug, Clone, Default)]
50pub struct TranslationOutput {
51    /// Generated Eggress TOML configuration.
52    pub toml: String,
53    /// Canonical typed findings; all other views derive from these.
54    pub issues: Vec<CompatIssue>,
55}
56
57impl TranslationOutput {
58    pub fn new(toml: String) -> Self {
59        Self {
60            toml,
61            issues: Vec::new(),
62        }
63    }
64
65    pub fn with_warning(mut self, category: &'static str, message: impl Into<String>) -> Self {
66        self.issues
67            .push(CompatIssue::warning_category(category, message));
68        self
69    }
70
71    pub fn with_unsupported(mut self, feature: &'static str, detail: impl Into<String>) -> Self {
72        self.issues
73            .push(CompatIssue::unsupported_feature(feature, detail));
74        self
75    }
76
77    pub fn with_warnings(mut self, warnings: Vec<CompatWarning>) -> Self {
78        self.issues
79            .extend(warnings.into_iter().map(CompatIssue::warning));
80        self
81    }
82
83    pub fn with_unsupported_features(mut self, features: Vec<UnsupportedFeature>) -> Self {
84        self.issues
85            .extend(features.into_iter().map(CompatIssue::unsupported));
86        self
87    }
88
89    /// Append already-typed issues (e.g. merging a sub-translation).
90    pub fn with_issues(mut self, issues: Vec<CompatIssue>) -> Self {
91        self.issues.extend(issues);
92        self
93    }
94
95    /// Legacy warning view: all `Warning`-severity issues, in order.
96    pub fn warnings(&self) -> Vec<CompatWarning> {
97        self.issues.iter().filter_map(|i| i.to_warning()).collect()
98    }
99
100    /// Legacy unsupported view: all `Unsupported`-severity issues, in order.
101    pub fn unsupported(&self) -> Vec<UnsupportedFeature> {
102        self.issues
103            .iter()
104            .filter_map(|i| i.to_unsupported())
105            .collect()
106    }
107
108    /// Structured diagnostics view for JSON/human rendering.
109    pub fn diagnostics(&self) -> Vec<StructuredDiagnostic> {
110        self.issues.iter().map(|i| i.to_diagnostic()).collect()
111    }
112
113    /// Whether any blocking (`Unsupported`) issue is present.
114    pub fn has_unsupported(&self) -> bool {
115        self.issues
116            .iter()
117            .any(|i| i.severity == IssueSeverity::Unsupported)
118    }
119
120    pub fn warnings_to_string(&self) -> String {
121        let mut out = String::new();
122        for w in self.warnings() {
123            out.push_str(&format!("{w}\n"));
124        }
125        for u in self.unsupported() {
126            out.push_str(&format!("{u}\n"));
127        }
128        out
129    }
130}