aqc_text_engine_core/requirement/
model.rs1#![allow(
4 clippy::module_name_repetitions,
5 reason = "Public text-engine values use the TextFile prefix."
6)]
7
8use core::any::Any;
9use core::cmp::Ordering;
10use core::fmt;
11
12use aqc_file_engine_core::merge::ResolvedAssertionOption;
13use aqc_file_engine_core::{
14 ConflictEntry, EngineRequirement, FileItemRequirement, ItemAssertionInput, ItemRequirements,
15 RequiredItemResolution, ResolvedItemRequirements, ResolvedRequirement, ScalarAssertion,
16 ScalarValue,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
20pub struct TextFileContents(Vec<u8>);
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum TextFileValueError {
26 Empty { field: &'static str },
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct TextFileRequirements {
33 pub exact_contents: Option<ScalarAssertion<TextFileContents>>,
35 pub contents: ItemRequirements<TextFileContents>,
37}
38
39#[derive(Debug, Clone, Default)]
40pub struct ResolvedTextFileRequirements {
42 pub(crate) exact_contents: ResolvedAssertionOption<ScalarAssertion<TextFileContents>>,
44 pub(crate) contents: ResolvedItemRequirements<TextFileContents>,
46}
47
48impl ResolvedTextFileRequirements {
49 #[must_use]
50 pub const fn exact_contents(
51 &self,
52 ) -> Option<
53 &ResolvedRequirement<ScalarAssertion<TextFileContents>, ScalarAssertion<TextFileContents>>,
54 > {
55 self.exact_contents.as_ref()
56 }
57
58 #[must_use]
59 pub const fn contents(&self) -> &ResolvedItemRequirements<TextFileContents> {
60 &self.contents
61 }
62}
63
64impl EngineRequirement for TextFileRequirements {
65 fn engine_id(&self) -> &'static str {
66 crate::ENGINE_ID
67 }
68
69 fn as_any(&self) -> &dyn Any {
70 self
71 }
72}
73
74impl TextFileContents {
75 pub fn new(value: impl Into<Vec<u8>>) -> Result<Self, TextFileValueError> {
81 let value = value.into();
82 if value.is_empty() {
83 return Err(TextFileValueError::Empty { field: "contents" });
84 }
85 Ok(Self(value))
86 }
87
88 #[must_use]
89 pub fn as_bytes(&self) -> &[u8] {
90 &self.0
91 }
92}
93
94impl fmt::Display for TextFileValueError {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 match self {
97 Self::Empty { field } => write!(f, "{field} must not be empty"),
98 }
99 }
100}
101
102impl fmt::Display for TextFileContents {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.write_str("contents")
105 }
106}
107
108impl ScalarValue for TextFileContents {
109 fn render(&self) -> String {
110 format!("{} bytes", self.0.len())
111 }
112
113 fn compare_for_order(&self, _other: &Self) -> Option<Ordering> {
114 None
115 }
116}
117
118impl FileItemRequirement for TextFileContents {
119 type Identity = Self;
120
121 fn merge_identity(&self) -> Self::Identity {
122 self.clone()
123 }
124
125 fn compose_item(
126 key: &str,
127 items: Vec<ItemAssertionInput<Self>>,
128 conflicts: &mut Vec<ConflictEntry>,
129 ) -> Option<RequiredItemResolution<Self>> {
130 aqc_file_engine_core::compose_item_by(key, items, Clone::clone, conflicts)
131 }
132}