Skip to main content

txtx_addon_kit/types/
types.rs

1use hcl_edit::expr::Expression;
2use hcl_edit::structure::Block;
3use indexmap::IndexMap;
4use jaq_interpret::Val;
5use serde::de::{self, MapAccess, Visitor};
6use serde::ser::SerializeMap;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use serde_json::{Map, Value as JsonValue};
9use std::collections::VecDeque;
10use std::fmt::{self, Debug};
11
12use crate::helpers::hcl::{
13    collect_constructs_references_from_block, collect_constructs_references_from_expression,
14    visit_optional_untyped_attribute,
15};
16use crate::types::frontend::{LogDetails, LogEvent, StaticLogEvent};
17use crate::types::ConstructDid;
18
19use super::diagnostics::Diagnostic;
20use super::{Did, EvaluatableInput};
21
22#[derive(Clone, Debug, Serialize)]
23#[serde(tag = "type", content = "value", rename_all = "lowercase")]
24pub enum Value {
25    Bool(bool),
26    Null,
27    #[serde(serialize_with = "i128_serializer")]
28    Integer(i128),
29    Float(f64),
30    String(String),
31    Array(Box<Vec<Value>>),
32    Object(IndexMap<String, Value>),
33    #[serde(serialize_with = "hex_serializer")]
34    Buffer(Vec<u8>),
35    #[serde(serialize_with = "addon_serializer")]
36    #[serde(untagged)]
37    Addon(AddonData),
38}
39
40impl PartialEq<Value> for Value {
41    fn eq(&self, other: &Value) -> bool {
42        match (self, other) {
43            (Value::Bool(lhs), Value::Bool(rhs)) => lhs == rhs,
44            (Value::Null, Value::Null) => true,
45            (Value::Integer(lhs), Value::Integer(rhs)) => lhs == rhs,
46            (Value::Float(lhs), Value::Float(rhs)) => lhs == rhs,
47            (Value::String(lhs), Value::String(rhs)) => lhs == rhs,
48            (Value::Buffer(lhs), Value::Buffer(rhs)) => lhs == rhs,
49            (Value::Object(lhs), Value::Object(rhs)) => {
50                if lhs.len() != rhs.len() {
51                    return false;
52                }
53                for (k, v) in lhs.iter() {
54                    if let Some(r) = rhs.get(k) {
55                        if v != r {
56                            return false;
57                        }
58                    } else {
59                        return false;
60                    }
61                }
62                true
63            }
64            (Value::Array(lhs), Value::Array(rhs)) => {
65                if lhs.len() != rhs.len() {
66                    return false;
67                }
68                for (l, r) in lhs.iter().zip(rhs.iter()) {
69                    if l != r {
70                        return false;
71                    }
72                }
73                true
74            }
75            (Value::Addon(lhs), Value::Addon(rhs)) => {
76                if lhs.id != rhs.id {
77                    return false;
78                }
79                lhs.bytes == rhs.bytes
80            }
81            _ => false,
82        }
83    }
84}
85
86fn i128_serializer<S>(value: &i128, ser: S) -> Result<S::Ok, S::Error>
87where
88    S: Serializer,
89{
90    ser.serialize_str(&value.to_string())
91}
92
93fn hex_serializer<S>(bytes: &Vec<u8>, ser: S) -> Result<S::Ok, S::Error>
94where
95    S: Serializer,
96{
97    let value = format!("0x{}", hex::encode(&bytes));
98    ser.serialize_str(&value)
99}
100
101fn addon_serializer<S>(addon_data: &AddonData, ser: S) -> Result<S::Ok, S::Error>
102where
103    S: Serializer,
104{
105    let mut map = ser.serialize_map(Some(2))?;
106    map.serialize_entry("type", &addon_data.id)?;
107    let value = format!("0x{}", hex::encode(&addon_data.bytes));
108    map.serialize_entry("value", &value)?;
109    map.end()
110}
111
112impl<'de> Deserialize<'de> for Value {
113    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
114    where
115        D: Deserializer<'de>,
116    {
117        struct ValueVisitor;
118
119        fn decode_hex_string(value: String) -> Result<Vec<u8>, String> {
120            let value = value.replace("0x", "");
121            hex::decode(&value)
122                .map_err(|e| format!("failed to decode hex string ({}) to bytes: {}", value, e))
123        }
124        impl<'de> Visitor<'de> for ValueVisitor {
125            type Value = Value;
126
127            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
128                formatter.write_str("a valid Value")
129            }
130
131            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
132            where
133                M: MapAccess<'de>,
134            {
135                let mut typing = None;
136                while let Some(key) = map.next_key::<String>()? {
137                    match key.as_str() {
138                        "type" => {
139                            if typing.is_some() {
140                                return Err(de::Error::duplicate_field("type"));
141                            }
142                            let the_typing = map.next_value::<String>()?;
143                            if the_typing.eq("null") {
144                                return Ok(Value::null());
145                            }
146                            typing = Some(the_typing);
147                        }
148                        "value" => {
149                            if typing.is_none() {
150                                let Some(key) = map.next_key::<String>()? else {
151                                    return Err(de::Error::missing_field("type"));
152                                };
153                                match key.as_str() {
154                                    "type" => {
155                                        let the_typing = map.next_value::<String>()?;
156                                        if the_typing.eq("null") {
157                                            return Ok(Value::null());
158                                        }
159                                        typing = Some(the_typing);
160                                    }
161                                    unexpected => {
162                                        return Err(de::Error::custom(format!(
163                                            "invalid Value: unexpected key {unexpected}"
164                                        )))
165                                    }
166                                };
167                            }
168                            let typing = typing.ok_or_else(|| de::Error::missing_field("type"))?;
169                            match typing.as_str() {
170                                "bool" => return Ok(Value::bool(map.next_value()?)),
171                                "integer" => {
172                                    let value: String = map.next_value()?;
173                                    let i128 = value.parse().map_err(serde::de::Error::custom)?;
174                                    return Ok(Value::integer(i128));
175                                }
176                                "float" => return Ok(Value::float(map.next_value()?)),
177                                "string" => return Ok(Value::string(map.next_value()?)),
178                                "null" => unreachable!(),
179                                "buffer" => {
180                                    let bytes =
181                                        decode_hex_string(map.next_value()?).map_err(|e| {
182                                            de::Error::custom(format!(
183                                                "failed to deserialize buffer: {e}"
184                                            ))
185                                        })?;
186                                    return Ok(Value::buffer(bytes));
187                                }
188                                "object" => return Ok(Value::object(map.next_value()?)),
189
190                                "array" => return Ok(Value::array(map.next_value()?)),
191                                other => {
192                                    if other.contains("::") {
193                                        let bytes =
194                                            decode_hex_string(map.next_value()?).map_err(|e| {
195                                                de::Error::custom(format!(
196                                                    "failed to deserialize buffer: {e}"
197                                                ))
198                                            })?;
199                                        return Ok(Value::addon(bytes, other));
200                                    } else {
201                                        return Err(de::Error::custom(format!(
202                                            "invalid type {other}"
203                                        )));
204                                    }
205                                }
206                            }
207                        }
208                        unexpected => {
209                            return Err(de::Error::custom(format!(
210                                "invalid Value: unexpected key {unexpected}"
211                            )));
212                        }
213                    }
214                }
215
216                Err(de::Error::custom("invalid Value: missing required key value"))
217            }
218        }
219
220        deserializer.deserialize_any(ValueVisitor)
221    }
222}
223
224pub type AddonJsonConverter<'a> = Box<dyn Fn(&Value) -> Result<Option<JsonValue>, Diagnostic> + 'a>;
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct RunbookCompleteAdditionalInfo {
228    /// The construct did that created this info
229    pub construct_did: ConstructDid,
230    /// The name of the construct that created this info
231    pub construct_name: String,
232    /// The title of the info
233    pub title: String,
234    /// The markdown details of the info
235    pub details: String,
236}
237
238impl RunbookCompleteAdditionalInfo {
239    pub const ADDON_ID: &str = "std::runbook_complete_additional_info";
240
241    pub fn new(
242        construct_did: &ConstructDid,
243        construct_name: impl ToString,
244        title: impl ToString,
245        details: impl ToString,
246    ) -> Self {
247        Self {
248            construct_did: construct_did.clone(),
249            construct_name: construct_name.to_string(),
250            title: title.to_string(),
251            details: details.to_string(),
252        }
253    }
254}
255
256impl Into<Vec<LogEvent>> for RunbookCompleteAdditionalInfo {
257    fn into(self) -> Vec<LogEvent> {
258        self.details
259            .split("\n")
260            .filter_map(|line| {
261                let trimmed = line.trim();
262                if trimmed.is_empty() {
263                    None
264                } else {
265                    Some(LogEvent::Static(StaticLogEvent {
266                        level: super::frontend::LogLevel::Info,
267                        details: LogDetails {
268                            message: trimmed.to_string(),
269                            summary: self.title.clone(),
270                        },
271                        uuid: self.construct_did.as_uuid(),
272                        namespace: self.construct_name.clone(),
273                    }))
274                }
275            })
276            .collect()
277    }
278}
279
280impl ToFromValue for RunbookCompleteAdditionalInfo {
281    fn to_value(&self) -> Value {
282        let serialized = serde_json::to_vec(self).unwrap();
283        Value::addon(serialized, RunbookCompleteAdditionalInfo::ADDON_ID)
284    }
285    fn from_value(value: &Value) -> Self {
286        let AddonData { bytes, id } = value.as_addon_data().unwrap();
287        if id != RunbookCompleteAdditionalInfo::ADDON_ID {
288            panic!("Value is not a RunbookCompleteAdditionalInfo");
289        }
290        serde_json::from_slice(bytes).unwrap()
291    }
292}
293
294pub trait ToFromValue {
295    fn to_value(&self) -> Value;
296    fn from_value(value: &Value) -> Self;
297}
298
299impl ToFromValue for Vec<RunbookCompleteAdditionalInfo> {
300    fn to_value(&self) -> Value {
301        let serialized = serde_json::to_vec(self).unwrap();
302        Value::addon(serialized, RunbookCompleteAdditionalInfo::ADDON_ID)
303    }
304    fn from_value(value: &Value) -> Self {
305        let AddonData { bytes, id } = value.as_addon_data().unwrap();
306        if id != RunbookCompleteAdditionalInfo::ADDON_ID {
307            panic!("Value is not a RunbookCompleteAdditionalInfo");
308        }
309        serde_json::from_slice(bytes).unwrap()
310    }
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub enum ThirdPartySignatureStatus {
315    Initialized,
316    Submitted,
317    CheckRequested,
318    Approved,
319    Rejected,
320}
321impl ThirdPartySignatureStatus {
322    pub fn to_bytes(&self) -> Vec<u8> {
323        match self {
324            ThirdPartySignatureStatus::Initialized => vec![0],
325            ThirdPartySignatureStatus::Submitted => vec![1],
326            ThirdPartySignatureStatus::CheckRequested => vec![2],
327            ThirdPartySignatureStatus::Approved => vec![3],
328            ThirdPartySignatureStatus::Rejected => vec![4],
329        }
330    }
331    pub fn from_bytes(bytes: &[u8]) -> Self {
332        match bytes {
333            [0] => ThirdPartySignatureStatus::Initialized,
334            [1] => ThirdPartySignatureStatus::Submitted,
335            [2] => ThirdPartySignatureStatus::CheckRequested,
336            [3] => ThirdPartySignatureStatus::Approved,
337            [4] => ThirdPartySignatureStatus::Rejected,
338            _ => panic!("Invalid bytes for ThirdPartySignatureStatus: {:?}", bytes),
339        }
340    }
341    pub fn is_approved(&self) -> bool {
342        matches!(self, ThirdPartySignatureStatus::Approved)
343    }
344    pub fn is_submitted(&self) -> bool {
345        matches!(self, ThirdPartySignatureStatus::Submitted)
346    }
347    pub fn is_check_requested(&self) -> bool {
348        matches!(self, ThirdPartySignatureStatus::CheckRequested)
349    }
350}
351pub const THIRD_PARTY_SIGNATURE: &str = "std::third_party_signature";
352
353impl Value {
354    pub fn third_party_signature_initialized() -> Self {
355        Value::addon(ThirdPartySignatureStatus::Initialized.to_bytes(), THIRD_PARTY_SIGNATURE)
356    }
357
358    pub fn third_party_signature_submitted() -> Self {
359        Value::addon(ThirdPartySignatureStatus::Submitted.to_bytes(), THIRD_PARTY_SIGNATURE)
360    }
361
362    pub fn third_party_signature_approved() -> Self {
363        Value::addon(ThirdPartySignatureStatus::Approved.to_bytes(), THIRD_PARTY_SIGNATURE)
364    }
365
366    pub fn third_party_signature_rejected() -> Self {
367        Value::addon(ThirdPartySignatureStatus::Rejected.to_bytes(), THIRD_PARTY_SIGNATURE)
368    }
369
370    pub fn third_party_signature_check_requested() -> Self {
371        Value::addon(ThirdPartySignatureStatus::CheckRequested.to_bytes(), THIRD_PARTY_SIGNATURE)
372    }
373
374    pub fn expect_third_party_signature(&self) -> ThirdPartySignatureStatus {
375        if let Value::Addon(addon_data) = self {
376            if addon_data.id == THIRD_PARTY_SIGNATURE {
377                return ThirdPartySignatureStatus::from_bytes(&addon_data.bytes);
378            }
379        }
380        panic!("Value is not a third party signature");
381    }
382
383    pub fn as_third_party_signature_status(&self) -> Option<ThirdPartySignatureStatus> {
384        if let Value::Addon(addon_data) = self {
385            if addon_data.id == THIRD_PARTY_SIGNATURE {
386                return Some(ThirdPartySignatureStatus::from_bytes(&addon_data.bytes));
387            }
388        }
389        None
390    }
391
392    pub fn as_runbook_complete_additional_info(&self) -> Option<RunbookCompleteAdditionalInfo> {
393        if let Value::Addon(addon_data) = self {
394            if addon_data.id == RunbookCompleteAdditionalInfo::ADDON_ID {
395                return Some(RunbookCompleteAdditionalInfo::from_value(&self));
396            }
397        }
398        None
399    }
400}
401
402impl Value {
403    pub fn string(value: String) -> Value {
404        Value::String(value.to_string())
405    }
406    pub fn integer(value: i128) -> Value {
407        Value::Integer(value)
408    }
409    pub fn float(value: f64) -> Value {
410        Value::Float(value)
411    }
412    pub fn null() -> Value {
413        Value::Null
414    }
415    pub fn bool(value: bool) -> Value {
416        Value::Bool(value)
417    }
418    pub fn buffer(bytes: Vec<u8>) -> Value {
419        Value::Buffer(bytes)
420    }
421    pub fn array(array: Vec<Value>) -> Value {
422        Value::Array(Box::new(array))
423    }
424    pub fn object(object: IndexMap<String, Value>) -> Value {
425        Value::Object(object)
426    }
427
428    pub fn addon(bytes: Vec<u8>, id: &str) -> Value {
429        Value::Addon(AddonData { bytes, id: id.to_string() })
430    }
431
432    pub fn expect_string(&self) -> &str {
433        match &self {
434            Value::String(value) => value,
435            _ => unreachable!(),
436        }
437    }
438    pub fn expect_integer(&self) -> i128 {
439        match &self {
440            Value::Integer(value) => *value,
441            _ => unreachable!(),
442        }
443    }
444    pub fn expect_uint(&self) -> Result<u64, String> {
445        match &self {
446            Value::Integer(value) => i128_to_u64(*value),
447            _ => unreachable!(),
448        }
449    }
450    pub fn expect_float(&self) -> f64 {
451        match &self {
452            Value::Float(value) => *value,
453            _ => unreachable!(),
454        }
455    }
456    pub fn expect_null(&self) -> () {
457        match &self {
458            Value::Null => (),
459            _ => unreachable!(),
460        }
461    }
462    pub fn expect_bool(&self) -> bool {
463        match &self {
464            Value::Bool(value) => *value,
465            _ => unreachable!(),
466        }
467    }
468    pub fn expect_buffer_data(&self) -> &Vec<u8> {
469        match &self {
470            Value::Buffer(value) => &value,
471            _ => unreachable!(),
472        }
473    }
474    pub fn expect_addon_data(&self) -> &AddonData {
475        match &self {
476            Value::Addon(value) => &value,
477            _ => unreachable!(),
478        }
479    }
480
481    #[deprecated(note = "use `get_buffer_bytes_result` instead")]
482    pub fn expect_buffer_bytes(&self) -> Vec<u8> {
483        self.try_get_buffer_bytes_result().unwrap().expect("unable to retrieve bytes")
484    }
485
486    pub fn get_buffer_bytes_result(&self) -> Result<Vec<u8>, String> {
487        self.try_get_buffer_bytes_result()?.ok_or("unable to retrieve bytes".into())
488    }
489
490    #[deprecated(note = "use `try_get_buffer_bytes_result` instead")]
491    pub fn try_get_buffer_bytes(&self) -> Option<Vec<u8>> {
492        let bytes = match &self {
493            Value::Buffer(value) => value.clone(),
494            Value::String(bytes) => {
495                let bytes = if bytes.starts_with("0x") {
496                    crate::hex::decode(&bytes[2..]).unwrap()
497                } else {
498                    crate::hex::decode(&bytes).unwrap()
499                };
500                bytes
501            }
502            Value::Array(values) => {
503                values.iter().flat_map(|v| v.get_buffer_bytes_result().unwrap()).collect()
504            }
505            Value::Addon(addon_value) => addon_value.bytes.clone(),
506            _ => return None,
507        };
508
509        Some(bytes)
510    }
511
512    pub fn try_get_buffer_bytes_result(&self) -> Result<Option<Vec<u8>>, String> {
513        let bytes = match &self {
514            Value::Buffer(value) => value.clone(),
515            Value::String(bytes) => {
516                let stripped = if bytes.starts_with("0x") { &bytes[2..] } else { &bytes[..] };
517                let bytes = crate::hex::decode(stripped).map_err(|e| {
518                    format!("string '{}' could not be decoded to hex bytes: {}", bytes, e)
519                })?;
520                bytes
521            }
522            Value::Array(values) => values
523                .iter()
524                .map(|v| v.try_get_buffer_bytes_result())
525                .collect::<Result<Vec<Option<_>>, String>>()?
526                .into_iter()
527                .filter_map(|v| v)
528                .flat_map(|v| v)
529                .collect(),
530            Value::Addon(addon_value) => addon_value.bytes.clone(),
531            _ => return Ok(None),
532        };
533
534        Ok(Some(bytes))
535    }
536    pub fn expect_array(&self) -> &Box<Vec<Value>> {
537        match &self {
538            Value::Array(value) => value,
539            _ => unreachable!(),
540        }
541    }
542
543    pub fn expect_object(&self) -> &IndexMap<String, Value> {
544        match &self {
545            Value::Object(value) => value,
546            _ => unreachable!(),
547        }
548    }
549
550    pub fn as_string(&self) -> Option<&str> {
551        match &self {
552            Value::String(value) => Some(value),
553            _ => None,
554        }
555    }
556    pub fn as_integer(&self) -> Option<i128> {
557        match &self {
558            Value::Integer(value) => Some(*value),
559            _ => None,
560        }
561    }
562    pub fn as_uint(&self) -> Option<Result<u64, String>> {
563        match &self {
564            Value::Integer(value) => Some(i128_to_u64(*value)),
565            _ => None,
566        }
567    }
568    pub fn as_u8(&self) -> Option<Result<u8, String>> {
569        match &self {
570            Value::Integer(value) => {
571                Some(u8::try_from(*value).map_err(|e| format!("invalid u8: {e}")))
572            }
573            _ => None,
574        }
575    }
576    pub fn as_u16(&self) -> Option<Result<u16, String>> {
577        match &self {
578            Value::Integer(value) => {
579                Some(u16::try_from(*value).map_err(|e| format!("invalid u16: {e}")))
580            }
581            _ => None,
582        }
583    }
584    pub fn as_float(&self) -> Option<f64> {
585        match &self {
586            Value::Float(value) => Some(*value),
587            _ => None,
588        }
589    }
590    pub fn as_null(&self) -> Option<()> {
591        match &self {
592            Value::Null => Some(()),
593            _ => None,
594        }
595    }
596    pub fn as_bool(&self) -> Option<bool> {
597        match &self {
598            Value::Bool(value) => Some(*value),
599            _ => None,
600        }
601    }
602    pub fn as_buffer_data(&self) -> Option<&Vec<u8>> {
603        match &self {
604            Value::Buffer(value) => Some(&value),
605            _ => None,
606        }
607    }
608    pub fn as_addon_data(&self) -> Option<&AddonData> {
609        match &self {
610            Value::Addon(value) => Some(&value),
611            _ => None,
612        }
613    }
614    pub fn as_array(&self) -> Option<&Box<Vec<Value>>> {
615        match &self {
616            Value::Array(value) => Some(value),
617            _ => None,
618        }
619    }
620
621    pub fn as_map(&self) -> Option<&Box<Vec<Value>>> {
622        match &self {
623            Value::Array(value) => Some(value),
624            _ => None,
625        }
626    }
627
628    pub fn as_object(&self) -> Option<&IndexMap<String, Value>> {
629        match &self {
630            Value::Object(value) => Some(value),
631            _ => None,
632        }
633    }
634
635    pub fn as_object_mut(&mut self) -> Option<&mut IndexMap<String, Value>> {
636        match self {
637            Value::Object(value) => Some(value),
638            _ => None,
639        }
640    }
641
642    pub fn get_keys_from_object(&self, mut keys: VecDeque<String>) -> Result<Value, Diagnostic> {
643        let Some(key) = keys.pop_front() else {
644            return Ok(self.clone());
645        };
646
647        if let Some(ref object) = self.as_object() {
648            match object.get(&key) {
649                Some(val) => val.get_keys_from_object(keys),
650                None => {
651                    Err(Diagnostic::error_from_string(format!("missing key '{}' from object", key)))
652                }
653            }
654        } else {
655            Err(Diagnostic::error_from_string(format!("invalid key '{}' for object", key)))
656        }
657    }
658
659    pub fn is_type_eq(&self, rhs: &Value) -> bool {
660        match (self, rhs) {
661            (Value::Null, Value::Null) => true,
662            (Value::Bool(_), Value::Bool(_)) => true,
663            (Value::Integer(_), Value::Integer(_)) => true,
664            (Value::Float(_), Value::Float(_)) => true,
665            (Value::String(_), Value::String(_)) => true,
666            (Value::Buffer(_), Value::Buffer(_)) => true,
667            (Value::Object(_), Value::Object(_)) => true,
668            (Value::Null, _) => false,
669            (Value::Bool(_), _) => false,
670            (Value::Integer(_), _) => false,
671            (Value::Float(_), _) => false,
672            (Value::String(_), _) => false,
673            (Value::Buffer(_), _) => false,
674            (Value::Object(_), _) => false,
675            (Value::Array(lhs), Value::Array(rhs)) => {
676                let Some(first_lhs) = lhs.first() else {
677                    return false;
678                };
679                let Some(first_rhs) = rhs.first() else {
680                    return false;
681                };
682                first_lhs.is_type_eq(first_rhs)
683            }
684            (Value::Array(_), _) => false,
685            (Value::Addon(lhs), Value::Addon(rhs)) => lhs.id == rhs.id,
686            (Value::Addon(_), _) => false,
687        }
688    }
689    pub fn to_be_bytes(&self) -> Vec<u8> {
690        match &self {
691            Value::Buffer(bytes) => bytes.clone(),
692            Value::Array(values) => {
693                let mut joined = vec![];
694                for value in values.iter() {
695                    joined.extend(value.to_be_bytes());
696                }
697                joined
698            }
699            Value::String(bytes) => {
700                let bytes = if bytes.starts_with("0x") {
701                    crate::hex::decode(&bytes[2..]).unwrap()
702                } else {
703                    match crate::hex::decode(&bytes) {
704                        Ok(res) => res,
705                        Err(_) => bytes.as_bytes().to_vec(),
706                    }
707                };
708                bytes
709            }
710            Value::Addon(data) => data.bytes.clone(),
711            Value::Integer(value) => value.to_be_bytes().to_vec(),
712            Value::Float(value) => value.to_be_bytes().to_vec(),
713            Value::Bool(value) => vec![*value as u8],
714            Value::Null => vec![],
715            Value::Object(values) => {
716                let mut joined = vec![];
717                for (key, value) in values.iter() {
718                    joined.extend(key.as_bytes());
719                    joined.extend(value.to_be_bytes());
720                }
721                joined
722            }
723        }
724    }
725
726    pub fn to_le_bytes(&self) -> Vec<u8> {
727        match &self {
728            Value::Buffer(bytes) => bytes.clone(),
729            Value::Array(values) => {
730                let mut joined = vec![];
731                for value in values.iter() {
732                    joined.extend(value.to_le_bytes());
733                }
734                joined
735            }
736            Value::String(bytes) => {
737                let bytes = if bytes.starts_with("0x") {
738                    crate::hex::decode(&bytes[2..]).unwrap()
739                } else {
740                    match crate::hex::decode(&bytes) {
741                        Ok(res) => res,
742                        Err(_) => bytes.as_bytes().to_vec(),
743                    }
744                };
745                bytes
746            }
747            Value::Addon(data) => data.bytes.clone(),
748            Value::Integer(value) => value.to_le_bytes().to_vec(),
749            Value::Float(value) => value.to_le_bytes().to_vec(),
750            Value::Bool(value) => vec![*value as u8],
751            Value::Null => vec![],
752            Value::Object(values) => {
753                let mut joined = vec![];
754                for (key, value) in values.iter() {
755                    joined.extend(key.as_bytes());
756                    joined.extend(value.to_le_bytes());
757                }
758                joined
759            }
760        }
761    }
762
763    pub fn parse_and_default_to_string(value_str: &str) -> Value {
764        let trim = value_str.trim();
765        let value = match trim.parse::<i128>() {
766            Ok(uint) => Value::integer(uint),
767            Err(_) => {
768                if trim.starts_with("[") || trim.starts_with("(") {
769                    let values_to_parse = trim[1..trim.len() - 1].split(",").collect::<Vec<_>>();
770                    let mut values = vec![];
771                    for v in values_to_parse.iter() {
772                        values.push(Value::parse_and_default_to_string(v));
773                    }
774                    Value::array(values)
775                } else {
776                    Value::string(trim.into())
777                }
778            }
779        };
780        value
781    }
782
783    pub fn compute_fingerprint(&self) -> Did {
784        let bytes = self.to_be_bytes();
785        Did::from_components(vec![bytes])
786    }
787
788    pub fn to_json(&self, addon_converters: Option<&Vec<AddonJsonConverter>>) -> JsonValue {
789        let json = match self {
790            Value::Bool(b) => JsonValue::Bool(*b),
791            Value::Null => JsonValue::Null,
792            Value::Integer(i) => JsonValue::Number(serde_json::Number::from(*i as i64)),
793            Value::Float(f) => JsonValue::Number(serde_json::Number::from_f64(*f).unwrap()),
794            Value::String(s) => JsonValue::String(s.to_string()),
795            Value::Array(vec) => JsonValue::Array(
796                vec.iter().map(|v| v.to_json(addon_converters)).collect::<Vec<JsonValue>>(),
797            ),
798            Value::Object(index_map) => JsonValue::Object(
799                index_map
800                    .iter()
801                    .map(|(k, v)| (k.clone(), v.to_json(addon_converters)))
802                    .collect::<Map<String, JsonValue>>(),
803            ),
804            Value::Buffer(vec) => JsonValue::String(format!("0x{}", hex::encode(&vec))),
805            Value::Addon(addon_data) => {
806                if let Some(addon_converters) = addon_converters.as_ref() {
807                    let parsed_values = addon_converters
808                        .iter()
809                        .filter_map(|converter| converter(self).ok().flatten())
810                        .collect::<Vec<_>>();
811                    if let Some(parsed_value) = parsed_values.first() {
812                        return parsed_value.clone();
813                    }
814                }
815                JsonValue::String(addon_data.to_string())
816            }
817        };
818        json
819    }
820}
821
822fn i128_to_u64(i128: i128) -> Result<u64, String> {
823    u64::try_from(i128).map_err(|e| format!("invalid uint: {e}"))
824}
825impl Value {
826    pub fn to_string(&self) -> String {
827        match self {
828            Value::String(val) => val.clone(),
829            Value::Bool(val) => val.to_string(),
830            Value::Integer(val) => val.to_string(),
831            Value::Float(val) => val.to_string(),
832            Value::Null => "null".to_string(),
833            Value::Buffer(bytes) => {
834                format!("0x{}", hex::encode(&bytes))
835            }
836            Value::Object(obj) => {
837                let mut res = "{".to_string();
838                let len = obj.len();
839                for (i, (k, v)) in obj.iter().enumerate() {
840                    res.push_str(&format!(
841                        r#""{}": {}{}"#,
842                        k,
843                        v.to_string(),
844                        if i == (len - 1) { "" } else { "," }
845                    ));
846                }
847                res
848            }
849            Value::Array(array) => {
850                format!("[{}]", array.iter().map(|e| e.to_string()).collect::<Vec<_>>().join(", "))
851            }
852            Value::Addon(addon_value) => addon_value.to_string(),
853        }
854    }
855
856    /// The same as [Value::to_string], but strings are wrapped in double quotes.
857    /// This is useful for generating JSON-formatted strings.
858    /// I don't know if there are side effects to the [Value::to_string] method having
859    /// the double quoted strings, so I'm keeping this separate for now.
860    pub fn encode_to_string(&self) -> String {
861        match self {
862            Value::String(val) => format!(r#""{val}""#),
863            Value::Bool(val) => val.to_string(),
864            Value::Integer(val) => val.to_string(),
865            Value::Float(val) => val.to_string(),
866            Value::Null => "null".to_string(),
867            Value::Buffer(bytes) => {
868                format!(r#""0x{}""#, hex::encode(&bytes))
869            }
870            Value::Object(obj) => {
871                let mut res = "{".to_string();
872                let len = obj.len();
873                for (i, (k, v)) in obj.iter().enumerate() {
874                    res.push_str(&format!(
875                        r#"
876    "{}": {}{}"#,
877                        k,
878                        v.encode_to_string(),
879                        if i == (len - 1) { "" } else { "," }
880                    ));
881                }
882                res.push_str(&format!(
883                    r#"
884}}"#
885                ));
886                res
887            }
888            Value::Array(array) => {
889                format!(
890                    "[{}]",
891                    array.iter().map(|e| e.encode_to_string()).collect::<Vec<_>>().join(", ")
892                )
893            }
894            Value::Addon(addon_value) => addon_value.encode_to_string(),
895        }
896    }
897
898    pub fn from_jaq_value(value: &Val) -> Result<Value, String> {
899        let res = match value {
900            Val::Null => Value::null(),
901            Val::Bool(val) => Value::bool(*val),
902            Val::Num(val) => val
903                .parse::<i128>()
904                .map(Value::integer)
905                .map_err(|e| format!("Failed to parse number: {}", e))?,
906            Val::Int(val) => i128::try_from(*val)
907                .map(Value::integer)
908                .map_err(|e| format!("Failed to convert integer: {}", e))?,
909            Val::Float(val) => Value::float(*val),
910            Val::Str(val) => Value::string(val.to_string()),
911            Val::Arr(val) => Value::array(
912                val.iter()
913                    .map(|v| Value::from_jaq_value(v))
914                    .collect::<Result<Vec<Value>, String>>()?,
915            ),
916            Val::Obj(val) => ObjectType::from(
917                val.iter()
918                    .map(|(k, v)| Value::from_jaq_value(v).map(|v| (k.as_str(), v)))
919                    .collect::<Result<Vec<(&str, Value)>, String>>()?,
920            )
921            .to_value(),
922        };
923        Ok(res)
924    }
925    pub fn get_type(&self) -> Type {
926        match self {
927            Value::Bool(_) => Type::Bool,
928            Value::Null => Type::null(),
929            Value::Integer(_) => Type::Integer,
930            Value::Float(_) => Type::Float,
931            Value::String(_) => Type::String,
932            Value::Buffer(_) => Type::Buffer,
933            Value::Object(_) => Type::Object(ObjectDefinition::arbitrary()),
934            Value::Array(t) => {
935                Type::Array(Box::new(t.first().unwrap_or(&Value::null()).get_type()))
936            }
937            Value::Addon(t) => Type::Addon(t.id.clone()),
938        }
939    }
940}
941
942// impl Serialize for PrimitiveValue {
943//     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
944//     where
945//         S: Serializer,
946//     {
947//         match self {
948//             PrimitiveValue::String(val) => serializer.serialize_str(val),
949//             PrimitiveValue::UnsignedInteger(val) => serializer.serialize_u64(*val),
950//             PrimitiveValue::SignedInteger(val) => serializer.serialize_i64(*val),
951//             PrimitiveValue::Float(val) => serializer.serialize_f64(*val),
952//             PrimitiveValue::Bool(val) => serializer.serialize_bool(*val),
953//             PrimitiveValue::Null => serializer.serialize_none(),
954//             PrimitiveValue::Buffer(BufferData { bytes, typing: _ }) => {
955//                 let mut s = String::from("0x");
956//                 s.write_str(
957//                     &bytes
958//                         .iter()
959//                         .map(|b| format!("{:02X}", b))
960//                         .collect::<String>(),
961//                 )
962//                 .unwrap();
963//                 serializer.serialize_str(&s)
964//             }
965//         }
966//     }
967// }
968
969#[derive(Clone, Debug)]
970pub struct ObjectType {
971    map: IndexMap<String, Value>,
972}
973impl ObjectType {
974    pub fn new() -> Self {
975        ObjectType { map: IndexMap::new() }
976    }
977
978    pub fn from_map(map: IndexMap<String, Value>) -> Self {
979        ObjectType { map }
980    }
981
982    pub fn from<S: ToString, T: IntoIterator<Item = (S, Value)>>(default: T) -> Self {
983        let mut map = IndexMap::new();
984        for (key, value) in default {
985            map.insert(key.to_string(), value);
986        }
987        ObjectType { map }
988    }
989
990    pub fn insert(&mut self, key: &str, value: Value) -> &mut Self {
991        self.map.insert(key.to_string(), value);
992        self
993    }
994
995    pub fn inner(&self) -> IndexMap<String, Value> {
996        self.map.clone()
997    }
998    pub fn to_value(&self) -> Value {
999        Value::object(self.map.clone())
1000    }
1001}
1002
1003#[derive(Clone, Serialize, Deserialize, PartialEq)]
1004pub struct AddonData {
1005    pub bytes: Vec<u8>,
1006    pub id: String,
1007}
1008impl AddonData {
1009    pub fn to_string(&self) -> String {
1010        format!("0x{}", hex::encode(&self.bytes))
1011    }
1012    pub fn encode_to_string(&self) -> String {
1013        format!(r#""0x{}""#, hex::encode(&self.bytes))
1014    }
1015}
1016
1017impl fmt::Debug for AddonData {
1018    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1019        // You can customize the output format here.
1020        f.debug_struct("AddonData").field("bytes", &self.to_string()).field("id", &self.id).finish()
1021    }
1022}
1023
1024#[derive(Clone, Debug, Eq, PartialEq, Hash)]
1025pub enum Type {
1026    Bool,
1027    Null(Option<Box<Type>>),
1028    Integer,
1029    Float,
1030    String,
1031    Buffer,
1032    Object(ObjectDefinition),
1033    Addon(String),
1034    Array(Box<Type>),
1035    Map(ObjectDefinition),
1036}
1037
1038impl Type {
1039    pub fn string() -> Type {
1040        Type::String
1041    }
1042    pub fn integer() -> Type {
1043        Type::Integer
1044    }
1045    pub fn float() -> Type {
1046        Type::Float
1047    }
1048    pub fn null() -> Type {
1049        Type::Null(None)
1050    }
1051    pub fn typed_null(inner: Type) -> Type {
1052        Type::Null(Some(Box::new(inner)))
1053    }
1054    pub fn bool() -> Type {
1055        Type::Bool
1056    }
1057    pub fn object(def: ObjectDefinition) -> Type {
1058        Type::Object(def)
1059    }
1060    pub fn strict_object(props: Vec<ObjectProperty>) -> Type {
1061        Type::Object(ObjectDefinition::strict(props))
1062    }
1063    pub fn arbitrary_object() -> Type {
1064        Type::Object(ObjectDefinition::arbitrary())
1065    }
1066    pub fn documented_arbitrary_object(props: Vec<ObjectProperty>) -> Type {
1067        Type::Object(ObjectDefinition::documented_arbitrary(props))
1068    }
1069    pub fn map(def: ObjectDefinition) -> Type {
1070        Type::Map(def)
1071    }
1072    pub fn strict_map(props: Vec<ObjectProperty>) -> Type {
1073        Type::Map(ObjectDefinition::strict(props))
1074    }
1075    pub fn arbitrary_map() -> Type {
1076        Type::Map(ObjectDefinition::arbitrary())
1077    }
1078    pub fn documented_arbitrary_map(props: Vec<ObjectProperty>) -> Type {
1079        Type::Map(ObjectDefinition::documented_arbitrary(props))
1080    }
1081    pub fn buffer() -> Type {
1082        Type::Buffer
1083    }
1084    pub fn addon(id: &str) -> Type {
1085        Type::Addon(id.to_string())
1086    }
1087    pub fn array(array_item_type: Type) -> Type {
1088        Type::Array(Box::new(array_item_type))
1089    }
1090
1091    pub fn check_value(&self, value: &Value) -> Result<(), Diagnostic> {
1092        let mismatch_err = |expected: &str| {
1093            Diagnostic::error_from_string(format!(
1094                "expected {}, got {}",
1095                expected,
1096                value.get_type().to_string()
1097            ))
1098        };
1099
1100        match &self {
1101            Type::Bool => value.as_bool().map(|_| ()).ok_or_else(|| mismatch_err("bool"))?,
1102            Type::Null(inner) => {
1103                if let Some(inner) = inner {
1104                    value
1105                        .as_null()
1106                        .map(|_| ())
1107                        .ok_or_else(|| mismatch_err(&format!("null<{}>", inner.to_string())))?
1108                } else {
1109                    value.as_null().map(|_| ()).ok_or_else(|| mismatch_err("null"))?
1110                }
1111            }
1112            Type::Integer => {
1113                value.as_integer().map(|_| ()).ok_or_else(|| mismatch_err("integer"))?
1114            }
1115            Type::Float => value.as_float().map(|_| ()).ok_or_else(|| mismatch_err("float"))?,
1116            Type::String => value.as_string().map(|_| ()).ok_or_else(|| mismatch_err("string"))?,
1117            Type::Buffer => {
1118                value.as_buffer_data().map(|_| ()).ok_or_else(|| mismatch_err("buffer"))?
1119            }
1120            Type::Addon(addon_type) => value
1121                .as_addon_data()
1122                .map(|_| ())
1123                .ok_or_else(|| mismatch_err(&format!("addon type '{}'", addon_type)))?,
1124            Type::Array(array_type) => value
1125                .as_array()
1126                .map(|_| ())
1127                .ok_or_else(|| mismatch_err(&format!("array<{}>", array_type.to_string())))?,
1128            Type::Object(object_def) | Type::Map(object_def) => match object_def {
1129                ObjectDefinition::Strict(props) => {
1130                    let object = value.as_object().ok_or_else(|| mismatch_err("object"))?;
1131                    for expected_prop in props.iter() {
1132                        let prop_value = object.get(&expected_prop.name);
1133                        if expected_prop.optional && prop_value.is_none() {
1134                            continue;
1135                        }
1136                        let prop_value = prop_value.ok_or_else(|| {
1137                            Diagnostic::error_from_string(format!(
1138                                "missing required property '{}'",
1139                                expected_prop.name,
1140                            ))
1141                        })?;
1142                        expected_prop.typing.check_value(prop_value).map_err(|e| {
1143                            Diagnostic::error_from_string(format!(
1144                                "object property '{}': {}",
1145                                expected_prop.name, e.message
1146                            ))
1147                        })?;
1148                    }
1149                }
1150                ObjectDefinition::Arbitrary(_) => {
1151                    let _ = value.as_object().ok_or_else(|| mismatch_err("object"))?;
1152                }
1153                ObjectDefinition::Tuple(_) | ObjectDefinition::Enum(_) => {
1154                    unimplemented!("ObjectDefinition::Tuple and ObjectDefinition::Enum are not supported for runbook types");
1155                }
1156            }, //  => todo!(),
1157        };
1158        Ok(())
1159    }
1160
1161    pub fn as_object(&self) -> Option<&ObjectDefinition> {
1162        match self {
1163            Type::Object(props) => Some(props),
1164            _ => None,
1165        }
1166    }
1167
1168    pub fn as_array(&self) -> Option<&Box<Type>> {
1169        match self {
1170            Type::Array(typing) => Some(typing),
1171            _ => None,
1172        }
1173    }
1174
1175    pub fn as_map(&self) -> Option<&ObjectDefinition> {
1176        match self {
1177            Type::Map(props) => Some(props),
1178            _ => None,
1179        }
1180    }
1181
1182    pub fn as_action(&self) -> Option<&String> {
1183        match self {
1184            Type::Addon(action) => Some(action),
1185            _ => None,
1186        }
1187    }
1188
1189    /// This function will get attributes from the provided HCL block that match the input name.
1190    /// It will collect all expressions in the block that reference other constructs, according
1191    /// to the rules defined by the `Type`.
1192    ///
1193    /// For example, while most types will just get the attribute value, the `Object` and `Map` types
1194    /// need to look for nested blocks and properties.
1195    pub fn get_expressions_referencing_constructs<'a>(
1196        &self,
1197        block: &Block,
1198        input: Box<dyn EvaluatableInput>,
1199        dependencies: &mut Vec<(Option<Box<dyn EvaluatableInput>>, Expression)>,
1200    ) {
1201        let input_name = input.name();
1202        match self {
1203            Type::Map(ref object_def) => match object_def {
1204                ObjectDefinition::Strict(props) => {
1205                    for block in block.body.get_blocks(&input_name) {
1206                        for prop in props.iter() {
1207                            let res = visit_optional_untyped_attribute(&prop.name, &block);
1208                            if let Some(expr) = res {
1209                                collect_constructs_references_from_expression(
1210                                    &expr,
1211                                    Some(input.clone()),
1212                                    dependencies,
1213                                );
1214                            }
1215                        }
1216                    }
1217                }
1218                ObjectDefinition::Arbitrary(_) => {
1219                    for block in block.body.get_blocks(&input_name) {
1220                        collect_constructs_references_from_block(
1221                            block,
1222                            Some(input.clone()),
1223                            dependencies,
1224                        );
1225                    }
1226                }
1227                ObjectDefinition::Tuple(_) | ObjectDefinition::Enum(_) => {
1228                    unimplemented!("ObjectDefinition::Tuple and ObjectDefinition::Enum are not supported for runbook types");
1229                }
1230            },
1231            Type::Object(ref object_def) => {
1232                if let Some(expr) = visit_optional_untyped_attribute(&input_name, &block) {
1233                    collect_constructs_references_from_expression(
1234                        &expr,
1235                        Some(input.clone()),
1236                        dependencies,
1237                    );
1238                }
1239                match object_def {
1240                    ObjectDefinition::Strict(props) => {
1241                        for prop in props.iter() {
1242                            for block in block.body.get_blocks(&input_name) {
1243                                if let Some(expr) =
1244                                    visit_optional_untyped_attribute(&prop.name, &block)
1245                                {
1246                                    collect_constructs_references_from_expression(
1247                                        &expr,
1248                                        Some(input.clone()),
1249                                        dependencies,
1250                                    );
1251                                }
1252                            }
1253                        }
1254                    }
1255                    ObjectDefinition::Arbitrary(_) => {
1256                        for block in block.body.get_blocks(&input_name) {
1257                            collect_constructs_references_from_block(
1258                                block,
1259                                Some(input.clone()),
1260                                dependencies,
1261                            );
1262                        }
1263                    }
1264                    ObjectDefinition::Tuple(_) | ObjectDefinition::Enum(_) => {
1265                        unimplemented!("ObjectDefinition::Tuple and ObjectDefinition::Enum are not supported for runbook types");
1266                    }
1267                }
1268            }
1269            _ => {
1270                if let Some(expr) = visit_optional_untyped_attribute(&input_name, &block) {
1271                    collect_constructs_references_from_expression(&expr, Some(input), dependencies);
1272                }
1273            }
1274        }
1275    }
1276}
1277
1278impl Type {
1279    pub fn to_string(&self) -> String {
1280        match self {
1281            Type::Bool => "bool".into(),
1282            Type::Null(inner) => {
1283                if let Some(inner) = inner {
1284                    format!("null<{}>", inner.to_string())
1285                } else {
1286                    "null".into()
1287                }
1288            }
1289            Type::Integer => "integer".into(),
1290            Type::Float => "float".into(),
1291            Type::String => "string".into(),
1292            Type::Buffer => "buffer".into(),
1293            Type::Object(_) => "object".into(),
1294            Type::Addon(addon) => format!("addon({})", addon),
1295            Type::Array(typing) => format!("array[{}]", typing.to_string()),
1296            Type::Map(_) => "map".into(),
1297        }
1298    }
1299}
1300
1301impl Default for Type {
1302    fn default() -> Self {
1303        Type::string()
1304    }
1305}
1306impl TryFrom<String> for Type {
1307    type Error = String;
1308    fn try_from(value: String) -> Result<Self, Self::Error> {
1309        let val = match value.as_str() {
1310            "string" => Type::String,
1311            "integer" => Type::Integer,
1312            "float" => Type::Float,
1313            "bool" => Type::Bool,
1314            "buffer" => Type::Buffer,
1315            "object" => Type::Object(ObjectDefinition::arbitrary()),
1316            other => {
1317                if other == "null" {
1318                    return Ok(Type::null());
1319                }
1320                if other.starts_with("null<") && other.ends_with(">") {
1321                    let mut inner = other.replace("null<", "");
1322                    inner = inner.replace(">", "");
1323                    let inner_type = Type::try_from(inner)?;
1324                    return Ok(Type::typed_null(inner_type));
1325                } else if other.starts_with("array[") && other.ends_with("]") {
1326                    let mut inner = other.replace("array[", "");
1327                    inner = inner.replace("]", "");
1328                    return Type::try_from(inner);
1329                } else if other.starts_with("addon(") {
1330                    let mut inner = other.replace("addon(", "");
1331                    inner = inner.replace(")", "");
1332                    Type::addon(&inner)
1333                } else {
1334                    return Err(format!("invalid type: {}", other));
1335                }
1336            }
1337        };
1338        Ok(val)
1339    }
1340}
1341
1342impl Serialize for Type {
1343    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1344    where
1345        S: Serializer,
1346    {
1347        serializer.serialize_str(&self.to_string())
1348    }
1349}
1350
1351impl<'de> Deserialize<'de> for Type {
1352    fn deserialize<D>(deserializer: D) -> Result<Type, D::Error>
1353    where
1354        D: Deserializer<'de>,
1355    {
1356        let type_str: String = serde::Deserialize::deserialize(deserializer)?;
1357        let t = Type::try_from(type_str).map_err(serde::de::Error::custom)?;
1358        Ok(t)
1359    }
1360}
1361
1362#[derive(Clone, Debug, Eq, PartialEq, Hash)]
1363pub enum ObjectDefinition {
1364    /// Strict object definition with a list of properties
1365    Strict(Vec<ObjectProperty>),
1366    /// Arbitrary object definition with no specific properties
1367    /// The optional list of object properties is used for documenting
1368    /// Some of the potential properties.
1369    Arbitrary(Option<Vec<ObjectProperty>>),
1370    /// Tuple variant for representing tuple types. This means that the 'name' field will be ignored.
1371    /// Instead, the index of the property will be used to create the tuple.
1372    Tuple(Vec<ObjectProperty>),
1373    /// Enum variant for representing enum types. This means that the value
1374    /// will have one of the specified properties.
1375    Enum(Vec<ObjectProperty>),
1376}
1377
1378impl ObjectDefinition {
1379    pub fn strict(props: Vec<ObjectProperty>) -> Self {
1380        ObjectDefinition::Strict(props)
1381    }
1382
1383    pub fn arbitrary() -> Self {
1384        ObjectDefinition::Arbitrary(None)
1385    }
1386
1387    pub fn documented_arbitrary(props: Vec<ObjectProperty>) -> Self {
1388        ObjectDefinition::Arbitrary(Some(props))
1389    }
1390
1391    pub fn tuple(props: Vec<ObjectProperty>) -> Self {
1392        ObjectDefinition::Tuple(props)
1393    }
1394
1395    pub fn enum_type(props: Vec<ObjectProperty>) -> Self {
1396        ObjectDefinition::Enum(props)
1397    }
1398
1399    pub fn join_documentation(&self, recursion_depth: usize) -> String {
1400        match self {
1401            ObjectDefinition::Strict(props) | ObjectDefinition::Arbitrary(Some(props)) => props
1402                .iter()
1403                .map(|prop| {
1404                    format!(
1405                        "{}- **{}**: {}",
1406                        " ".repeat((recursion_depth + 1) * 2),
1407                        prop.name,
1408                        prop.join_documentation(recursion_depth + 1)
1409                    )
1410                })
1411                .collect::<Vec<String>>()
1412                .join("\n"),
1413            ObjectDefinition::Arbitrary(None) => String::new(),
1414            _ => {
1415                // For Tuple and Enum, we don't have a specific documentation format
1416                // so we return an empty string.
1417                String::new()
1418            }
1419        }
1420    }
1421}
1422
1423#[derive(Clone, Debug, Eq, PartialEq, Hash)]
1424pub struct ObjectProperty {
1425    pub name: String,
1426    pub documentation: String,
1427    pub typing: Type,
1428    pub optional: bool,
1429    pub tainting: bool,
1430    pub internal: bool,
1431}
1432
1433impl ObjectProperty {
1434    pub fn join_documentation(&self, recursion_depth: usize) -> String {
1435        match &self.typing {
1436            Type::Object(object_definition) => {
1437                format!(
1438                    "{} This is an object type containing the keys:\n{}",
1439                    self.documentation,
1440                    object_definition.join_documentation(recursion_depth)
1441                )
1442            }
1443            Type::Map(object_definition) => {
1444                format!(
1445                    "{} This is a map type containing the keys:\n{}",
1446                    self.documentation,
1447                    object_definition.join_documentation(recursion_depth)
1448                )
1449            }
1450            _ => self.documentation.clone(),
1451        }
1452    }
1453}
1454
1455#[derive(Clone, Debug)]
1456pub struct RunbookSupervisionContext {
1457    pub review_input_default_values: bool,
1458    pub review_input_values: bool,
1459    pub is_supervised: bool,
1460}
1461
1462impl RunbookSupervisionContext {
1463    pub fn new() -> Self {
1464        Self {
1465            review_input_default_values: false,
1466            review_input_values: false,
1467            is_supervised: false,
1468        }
1469    }
1470}