Skip to main content

greentic_types/messaging/
rendering.rs

1//! Provider-agnostic DTOs for host messaging render plans.
2//!
3//! These types describe renderer modes, capability profiles, and diagnostics for adapters and
4//! host-side logic. They keep `greentic-types` strictly declarative; actual downgrade and rendering
5//! decisions live in the caller implementations, and providers may ignore hints they do not support.
6
7extern crate alloc;
8
9use alloc::{string::String, vec::Vec};
10
11#[cfg(feature = "schemars")]
12use schemars::JsonSchema;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16fn is_false(value: &bool) -> bool {
17    !*value
18}
19
20/// Stable renderer modes hosts can request from providers.
21#[derive(Clone, Debug, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23#[cfg_attr(feature = "schemars", derive(JsonSchema))]
24#[cfg_attr(feature = "serde", serde(tag = "mode", rename_all = "snake_case"))]
25pub enum RendererMode {
26    /// Forward the card/message exactly as produced.
27    Passthrough,
28    /// Downgrade to plain text (Tier D behavior).
29    TextOnly,
30    /// Downgrade adaptive cards to a safer version before sending to the provider.
31    AdaptiveCardDowngrade {
32        /// Target adaptive card version (e.g. "1.4").
33        target_version: AdaptiveCardVersion,
34        /// Fail on unsupported features when `true`, otherwise best-effort downgrade.
35        #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
36        strict: bool,
37    },
38}
39
40/// Wrapper for adaptive card schema versions so DTO contracts remain explicit even if no validation is applied.
41#[derive(Clone, Debug, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43#[cfg_attr(feature = "schemars", derive(JsonSchema))]
44#[cfg_attr(feature = "serde", serde(transparent))]
45pub struct AdaptiveCardVersion(String);
46
47impl AdaptiveCardVersion {
48    /// Returns the inner version string slice.
49    pub fn as_str(&self) -> &str {
50        &self.0
51    }
52}
53
54impl From<String> for AdaptiveCardVersion {
55    fn from(value: String) -> Self {
56        Self(value)
57    }
58}
59
60impl From<&str> for AdaptiveCardVersion {
61    fn from(value: &str) -> Self {
62        Self(value.to_owned())
63    }
64}
65
66impl AsRef<str> for AdaptiveCardVersion {
67    fn as_ref(&self) -> &str {
68        self.as_str()
69    }
70}
71
72impl core::fmt::Display for AdaptiveCardVersion {
73    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74        write!(f, "{}", self.0)
75    }
76}
77
78/// Provider-agnostic render tiers used for diagnostics and plans.
79#[derive(Clone, Debug, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81#[cfg_attr(feature = "schemars", derive(JsonSchema))]
82#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
83pub enum Tier {
84    /// Tier A renders the full adaptive card experience when the destination fully supports it.
85    TierA,
86    /// Tier B targets partial rendering when some features may be missing.
87    TierB,
88    /// Tier C offers simplified layouts or read-only cards where interactions are limited.
89    TierC,
90    /// Tier D downgrades to plain text or minimal content for the most restrictive destinations.
91    TierD,
92}
93
94/// Capabilities associated with a destination/provider.
95#[derive(Clone, Debug, Default, PartialEq, Eq)]
96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97#[cfg_attr(feature = "schemars", derive(JsonSchema))]
98pub struct CapabilityProfile {
99    /// Maximum adaptive card version the destination declares support for.
100    #[cfg_attr(
101        feature = "serde",
102        serde(default, skip_serializing_if = "Option::is_none")
103    )]
104    pub max_adaptive_card_version: Option<AdaptiveCardVersion>,
105    /// Whether adaptive cards are supported (unknown when `None`).
106    #[cfg_attr(
107        feature = "serde",
108        serde(default, skip_serializing_if = "Option::is_none")
109    )]
110    pub supports_adaptive_cards: Option<bool>,
111    /// Whether interactive actions (e.g. submit, openUrl) are supported.
112    #[cfg_attr(
113        feature = "serde",
114        serde(default, skip_serializing_if = "Option::is_none")
115    )]
116    pub supports_actions: Option<bool>,
117    /// Whether media playback/gifs are supported by the destination.
118    #[cfg_attr(
119        feature = "serde",
120        serde(default, skip_serializing_if = "Option::is_none")
121    )]
122    pub supports_media: Option<bool>,
123    /// Whether input controls (choice set, date picker) are supported.
124    #[cfg_attr(
125        feature = "serde",
126        serde(default, skip_serializing_if = "Option::is_none")
127    )]
128    pub supports_input_controls: Option<bool>,
129    /// Whether Markdown formatting is supported.
130    #[cfg_attr(
131        feature = "serde",
132        serde(default, skip_serializing_if = "Option::is_none")
133    )]
134    pub supports_markdown: Option<bool>,
135}
136
137/// Diagnostics attached to a render plan for logs and tests.
138#[derive(Clone, Debug, Default, PartialEq, Eq)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140#[cfg_attr(feature = "schemars", derive(JsonSchema))]
141pub struct RenderDiagnostics {
142    /// Optional tier-level summary for diagnostics.
143    #[cfg_attr(
144        feature = "serde",
145        serde(default, skip_serializing_if = "Option::is_none")
146    )]
147    pub tier: Option<Tier>,
148    /// Warning messages describing graceful degradations.
149    #[cfg_attr(
150        feature = "serde",
151        serde(default, skip_serializing_if = "Vec::is_empty")
152    )]
153    pub warnings: Vec<String>,
154    /// Error messages describing failures or skipped content.
155    #[cfg_attr(
156        feature = "serde",
157        serde(default, skip_serializing_if = "Vec::is_empty")
158    )]
159    pub errors: Vec<String>,
160}
161
162/// Hints that can be attached to render plans without coupling to a specific provider implementation.
163#[derive(Clone, Debug, Default, PartialEq, Eq)]
164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
165#[cfg_attr(feature = "schemars", derive(JsonSchema))]
166pub struct RenderPlanHints {
167    /// Desired renderer mode requested by the host.
168    #[cfg_attr(
169        feature = "serde",
170        serde(default, skip_serializing_if = "Option::is_none")
171    )]
172    pub renderer_mode: Option<RendererMode>,
173    /// Capability profile for the target destination or channel.
174    #[cfg_attr(
175        feature = "serde",
176        serde(default, skip_serializing_if = "Option::is_none")
177    )]
178    pub capability_profile: Option<CapabilityProfile>,
179    /// Diagnostics emitted while producing the plan.
180    #[cfg_attr(
181        feature = "serde",
182        serde(default, skip_serializing_if = "Option::is_none")
183    )]
184    pub diagnostics: Option<RenderDiagnostics>,
185    /// Optional tier summary for the plan body.
186    #[cfg_attr(
187        feature = "serde",
188        serde(default, skip_serializing_if = "Option::is_none")
189    )]
190    pub tier: Option<Tier>,
191}