1use std::{collections::BTreeMap, fmt};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
6#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
8pub struct Position {
9 pub line: usize,
10 pub column: usize,
11 pub offset: usize,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
18pub struct Diagnostic {
19 pub code: String,
20 pub message: String,
21 pub position: Position,
22}
23impl Diagnostic {
24 pub(crate) fn new(code: &str, message: impl Into<String>, position: Position) -> Self {
25 Self {
26 code: code.into(),
27 message: message.into(),
28 position,
29 }
30 }
31}
32impl fmt::Display for Diagnostic {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(
35 f,
36 "{}:{}: {}: {}",
37 self.position.line, self.position.column, self.code, self.message
38 )
39 }
40}
41impl std::error::Error for Diagnostic {}
42
43#[derive(Debug, Clone, PartialEq, Eq, Default)]
45#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
46#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
47#[cfg_attr(feature = "json", serde(deny_unknown_fields))]
48pub struct Limits {
49 pub max_tokens: Option<u64>,
50 pub per_item: Option<u64>,
52 pub reason: Option<String>,
54 pub sig: Option<String>,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
61#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
62#[cfg_attr(
63 feature = "json",
64 serde(tag = "kind", rename_all = "lowercase", deny_unknown_fields)
65)]
66pub enum Node {
67 Text { value: String },
68 Element(Element),
69 Variable(Variable),
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
74#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
75#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
76#[cfg_attr(feature = "json", serde(deny_unknown_fields))]
77pub struct Element {
78 pub name: String,
79 pub id: Option<String>,
80 pub limits: Limits,
81 pub children: Vec<Node>,
82 pub position: Position,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
92#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
93#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
94#[cfg_attr(feature = "json", serde(deny_unknown_fields))]
95pub struct Variable {
96 pub id: String,
97 pub position: Position,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
103#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
104#[cfg_attr(feature = "json", serde(deny_unknown_fields))]
105pub struct Document {
106 pub version: String,
107 pub tokenizer: String,
108 pub limits: Limits,
109 pub children: Vec<Node>,
110 pub position: Position,
111}
112
113pub type Bindings = BTreeMap<String, String>;
115
116#[derive(Debug, Clone, Copy)]
119pub enum ElementRef<'a> {
120 Element(&'a Element),
121 Variable(&'a Variable),
122}
123
124fn lookup<'a>(nodes: &'a [Node], id: &str) -> Option<ElementRef<'a>> {
125 for node in nodes {
126 match node {
127 Node::Element(s) => {
128 if s.id.as_deref() == Some(id) {
129 return Some(ElementRef::Element(s));
130 }
131 if let Some(found) = lookup(&s.children, id) {
132 return Some(found);
133 }
134 }
135 Node::Variable(v) if v.id == id => return Some(ElementRef::Variable(v)),
136 _ => {}
137 }
138 }
139 None
140}
141fn elements(nodes: &[Node]) -> Vec<&Element> {
142 nodes
143 .iter()
144 .filter_map(|n| {
145 if let Node::Element(s) = n {
146 Some(s)
147 } else {
148 None
149 }
150 })
151 .collect()
152}
153fn display_nodes(nodes: &[Node], f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 for n in nodes {
155 match n {
156 Node::Text { value } => f.write_str(value)?,
157 Node::Element(s) => display_nodes(&s.children, f)?,
158 Node::Variable(v) => write!(f, "{{{{{}}}}}", v.id)?,
160 }
161 }
162 Ok(())
163}
164impl Document {
165 pub fn new(max_tokens: u64, reason: impl Into<String>, children: Vec<Node>) -> Self {
167 Self {
168 version: "0.3".into(),
169 tokenizer: "cl100k_base".into(),
170 limits: Limits {
171 max_tokens: Some(max_tokens),
172 per_item: None,
173 reason: Some(reason.into()),
174 sig: None,
175 },
176 children,
177 position: Position::default(),
178 }
179 }
180 pub fn get_element_by_id(&self, id: &str) -> Option<ElementRef<'_>> {
181 lookup(&self.children, id)
182 }
183 pub fn elements(&self) -> Vec<&Element> {
184 elements(&self.children)
185 }
186 pub fn elements_by_name(&self, name: &str) -> Vec<&Element> {
187 elements_by_name(&self.children, name)
188 }
189 pub fn element(mut self, element: Element) -> Self {
190 self.children.push(Node::Element(element));
191 self
192 }
193 pub fn render(
195 &self,
196 bindings: &Bindings,
197 counter: &impl crate::TokenCounter,
198 ) -> Result<crate::RenderedDocument, Vec<Diagnostic>> {
199 crate::render_checked(self, bindings, counter)
200 }
201}
202impl Element {
203 pub fn new(name: impl Into<String>) -> Self {
205 Self {
206 name: name.into(),
207 id: None,
208 limits: Limits::default(),
209 children: Vec::new(),
210 position: Position::default(),
211 }
212 }
213 pub fn id(mut self, id: impl Into<String>) -> Self {
214 self.id = Some(id.into());
215 self
216 }
217 pub fn text(mut self, value: impl Into<String>) -> Self {
219 self.children.push(Node::text(value));
220 self
221 }
222 pub fn variable(mut self, name: impl Into<String>) -> Self {
223 self.children.push(Node::variable(name));
224 self
225 }
226 pub fn template(mut self, value: &str) -> Result<Self, Diagnostic> {
228 self.children.extend(crate::parse_template(value)?);
229 Ok(self)
230 }
231 pub fn element(mut self, element: Element) -> Self {
232 self.children.push(Node::Element(element));
233 self
234 }
235 pub fn max_tokens(mut self, tokens: u64, reason: impl Into<String>) -> Self {
236 self.limits.max_tokens = Some(tokens);
237 self.limits.reason = Some(reason.into());
238 self.limits.sig = None;
239 self
240 }
241 pub fn per_item(mut self, tokens: u64, reason: impl Into<String>) -> Self {
242 self.limits.per_item = Some(tokens);
243 self.limits.reason = Some(reason.into());
244 self.limits.sig = None;
245 self
246 }
247 pub fn get_element_by_id(&self, id: &str) -> Option<ElementRef<'_>> {
248 lookup(&self.children, id)
249 }
250 pub fn elements(&self) -> Vec<&Element> {
251 elements(&self.children)
252 }
253 pub fn elements_by_name(&self, name: &str) -> Vec<&Element> {
254 elements_by_name(&self.children, name)
255 }
256}
257fn elements_by_name<'a>(nodes: &'a [Node], name: &str) -> Vec<&'a Element> {
258 let mut found = Vec::new();
259 let mut pending: Vec<_> = nodes.iter().rev().collect();
260 while let Some(node) = pending.pop() {
261 if let Node::Element(element) = node {
262 if element.name == name {
263 found.push(element);
264 }
265 pending.extend(element.children.iter().rev());
266 }
267 }
268 found
269}
270impl Node {
271 pub fn text(value: impl Into<String>) -> Self {
272 Self::Text {
273 value: value.into(),
274 }
275 }
276 pub fn variable(id: impl Into<String>) -> Self {
277 Self::Variable(Variable {
278 id: id.into(),
279 position: Position::default(),
280 })
281 }
282}
283impl From<Element> for Node {
284 fn from(element: Element) -> Self {
285 Self::Element(element)
286 }
287}
288impl fmt::Display for Element {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 display_nodes(&self.children, f)
291 }
292}
293impl fmt::Display for Document {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 display_nodes(&self.children, f)
296 }
297}
298impl fmt::Display for ElementRef<'_> {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 match self {
301 Self::Element(s) => s.fmt(f),
302 Self::Variable(v) => write!(f, "{{{{{}}}}}", v.id),
303 }
304 }
305}
306
307#[derive(Debug, Clone)]
310#[cfg_attr(feature = "json", derive(serde::Serialize))]
311#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
312pub struct Measurement {
313 pub name: String,
314 pub path: Vec<usize>,
316 pub id: Option<String>,
317 pub tokens: Option<u64>,
318 pub limit: Option<u64>,
319 pub deferred: bool,
320 pub reason: Option<String>,
321}
322#[derive(Debug, Clone, Default)]
323#[cfg_attr(feature = "json", derive(serde::Serialize))]
324#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
325pub struct Report {
326 pub diagnostics: Vec<Diagnostic>,
327 pub measurements: Vec<Measurement>,
328}
329impl Report {
330 pub fn is_ok(&self) -> bool {
332 self.diagnostics.is_empty()
333 }
334}