1pub mod unstable;
13
14use std::{
15 borrow::Cow,
16 collections::HashSet,
17 error::Error,
18 fmt::{self, Write},
19 hash::{Hash, Hasher},
20 vec,
21};
22
23use ReprPiece::{Child, Text};
24use chrono::{NaiveDate, NaiveDateTime, NaiveTime, TimeDelta as ChronoTimeDelta};
25use num_bigint::BigInt;
26use num_traits::{ToPrimitive, Zero};
27
28use crate::{
29 builtins::BuiltinsFunctions,
30 exceptions::ExcType,
31 file_mode::FileMode,
32 format::{FormatComplex, FormatFloat, StringRepr, bytes_repr_fmt, format_offset_timedelta_repr, string_repr_fmt},
33 graph::{ClassTypeNode, GraphError, MontyGraph, MontyNode, NodeId},
34 resource::ResourceError,
35 unstable::PushValue,
36 uuid::MontyUuid,
37};
38
39#[derive(Debug, Clone, Eq, serde::Serialize, serde::Deserialize)]
45pub struct MontyObject {
46 graph: MontyGraph,
48 root: NodeId,
50}
51
52impl MontyObject {
53 fn new(graph: MontyGraph, root: NodeId) -> Result<Self, GraphError> {
55 graph.check_root(root)?;
56 Ok(Self { graph, root })
57 }
58
59 #[must_use]
64 fn leaf(node: MontyNode) -> Self {
65 let mut graph = MontyGraph::with_capacity(1);
66 let root = graph.push(node);
67 Self { graph, root }
68 }
69
70 #[must_use]
72 pub fn none() -> Self {
73 Self::leaf(MontyNode::None)
74 }
75
76 #[must_use]
78 pub fn ellipsis() -> Self {
79 Self::leaf(MontyNode::Ellipsis)
80 }
81
82 #[must_use]
84 pub fn not_implemented() -> Self {
85 Self::leaf(MontyNode::NotImplemented)
86 }
87
88 #[must_use]
90 pub fn bool(value: bool) -> Self {
91 Self::leaf(MontyNode::Bool(value))
92 }
93
94 #[must_use]
96 pub fn int(value: i64) -> Self {
97 Self::leaf(MontyNode::Int(value))
98 }
99
100 #[must_use]
102 pub fn bigint(value: BigInt) -> Self {
103 Self::leaf(MontyNode::BigInt(value))
104 }
105
106 #[must_use]
108 pub fn float(value: f64) -> Self {
109 Self::leaf(MontyNode::Float(value))
110 }
111
112 #[must_use]
114 pub fn complex(real: f64, imag: f64) -> Self {
115 Self::leaf(MontyNode::Complex(MontyComplex { real, imag }))
116 }
117
118 #[must_use]
120 pub fn string(value: impl Into<String>) -> Self {
121 Self::leaf(MontyNode::String(value.into()))
122 }
123
124 #[must_use]
126 pub fn bytes(value: impl Into<Vec<u8>>) -> Self {
127 Self::leaf(MontyNode::Bytes(value.into()))
128 }
129
130 #[must_use]
132 pub fn path(value: impl Into<String>) -> Self {
133 Self::leaf(MontyNode::Path(value.into()))
134 }
135
136 #[must_use]
138 pub fn date(value: MontyDate) -> Self {
139 Self::leaf(MontyNode::Date(value))
140 }
141
142 #[must_use]
144 pub fn datetime(value: MontyDateTime) -> Self {
145 Self::leaf(MontyNode::DateTime(value))
146 }
147
148 #[must_use]
150 pub fn time(value: MontyTime) -> Self {
151 Self::leaf(MontyNode::Time(value))
152 }
153
154 #[must_use]
156 pub fn timedelta(value: MontyTimeDelta) -> Self {
157 Self::leaf(MontyNode::TimeDelta(value))
158 }
159
160 #[must_use]
162 pub fn timezone(value: MontyTimeZone) -> Self {
163 Self::leaf(MontyNode::TimeZone(value))
164 }
165
166 #[must_use]
171 pub fn decimal(value: impl Into<String>) -> Self {
172 Self::leaf(MontyNode::Decimal(value.into()))
173 }
174
175 #[must_use]
177 pub fn exception(exc_type: ExcType, arg: Option<String>) -> Self {
178 Self::leaf(MontyNode::Exception { exc_type, arg })
179 }
180
181 #[must_use]
183 pub fn function(name: impl Into<String>, docstring: Option<String>) -> Self {
184 Self::leaf(MontyNode::Function {
185 name: name.into(),
186 docstring,
187 })
188 }
189
190 #[must_use]
192 pub fn builtin_function(function: BuiltinsFunctions) -> Self {
193 Self::leaf(MontyNode::BuiltinFunction(function))
194 }
195
196 #[must_use]
198 pub fn type_object(value: MontyType) -> Self {
199 Self::leaf(MontyNode::Type(value))
200 }
201
202 #[must_use]
204 pub fn file_handle(value: MontyFileHandle) -> Self {
205 Self::leaf(MontyNode::FileHandle(value))
206 }
207
208 #[must_use]
210 pub fn repr(value: impl Into<String>) -> Self {
211 Self::leaf(MontyNode::Repr(value.into()))
212 }
213
214 #[must_use]
216 pub fn cycle(placeholder: impl Into<String>) -> Self {
217 Self::leaf(MontyNode::Cycle(placeholder.into()))
218 }
219
220 #[must_use]
222 pub fn list(items: impl IntoIterator<Item = Self>) -> Self {
223 Self::container(items, MontyNode::List)
224 }
225
226 #[must_use]
228 pub fn tuple(items: impl IntoIterator<Item = Self>) -> Self {
229 Self::container(items, MontyNode::Tuple)
230 }
231
232 #[must_use]
234 pub fn set(items: impl IntoIterator<Item = Self>) -> Self {
235 Self::container(items, MontyNode::Set)
236 }
237
238 #[must_use]
240 pub fn frozenset(items: impl IntoIterator<Item = Self>) -> Self {
241 Self::container(items, MontyNode::FrozenSet)
242 }
243
244 #[must_use]
246 pub fn dict(pairs: impl IntoIterator<Item = (Self, Self)>) -> Self {
247 let mut graph = MontyGraph::new();
248 let pairs = push_pairs(pairs, &mut graph);
249 let root = graph.push(MontyNode::Dict(pairs));
250 Self { graph, root }
251 }
252
253 #[must_use]
255 pub fn named_tuple(
256 type_name: impl Into<String>,
257 field_names: impl IntoIterator<Item = impl Into<String>>,
258 values: impl IntoIterator<Item = Self>,
259 ) -> Self {
260 let type_name = type_name.into();
261 let field_names = field_names.into_iter().map(Into::into).collect();
262 Self::container(values, |values| MontyNode::NamedTuple {
263 type_name,
264 field_names,
265 values,
266 })
267 }
268
269 #[must_use]
275 pub fn class_type(
276 name: impl Into<String>,
277 id: MontyUuid,
278 host_defined: bool,
279 is_dataclass: bool,
280 attrs: impl IntoIterator<Item = (Self, Self)>,
281 ) -> Self {
282 let mut graph = MontyGraph::new();
283 let attrs = push_pairs(attrs, &mut graph);
284 let root = graph.push(MontyNode::ClassType(Box::new(ClassTypeNode {
285 name: name.into(),
286 id,
287 host_defined,
288 is_dataclass,
289 attrs,
290 })));
291 Self { graph, root }
292 }
293
294 #[must_use]
300 pub fn class_instance(
301 class_type: Self,
302 instance_id: MontyUuid,
303 attrs: impl IntoIterator<Item = (Self, Self)>,
304 ) -> Self {
305 let mut graph = MontyGraph::new();
306 let class_type = class_type.push_into(&mut graph);
307 let attrs = push_pairs(attrs, &mut graph);
308 let root = graph.push(MontyNode::ClassInstance {
309 class_type,
310 instance_id,
311 attrs,
312 });
313 Self { graph, root }
314 }
315
316 #[must_use]
319 pub fn module(name: impl Into<String>, id: MontyUuid, attrs: impl IntoIterator<Item = (Self, Self)>) -> Self {
320 let mut graph = MontyGraph::new();
321 let attrs = push_pairs(attrs, &mut graph);
322 let root = graph.push(MontyNode::Module {
323 name: name.into(),
324 id,
325 attrs,
326 });
327 Self { graph, root }
328 }
329
330 #[must_use]
333 pub fn builtin_function_from_name(name: &str) -> Option<Self> {
334 name.parse::<BuiltinsFunctions>().ok().map(Self::builtin_function)
335 }
336
337 #[must_use]
339 pub fn as_ref(&self) -> ObjectRef<'_> {
340 ObjectRef {
341 graph: &self.graph,
342 id: self.root,
343 }
344 }
345
346 #[must_use]
348 fn root_node(&self) -> &MontyNode {
349 self.graph.node(self.root)
350 }
351
352 #[must_use]
354 pub fn py_repr(&self) -> String {
355 self.as_ref().py_repr()
356 }
357
358 #[must_use]
360 pub fn is_truthy(&self) -> bool {
361 self.as_ref().is_truthy()
362 }
363
364 #[must_use]
366 pub fn type_name(&self) -> &str {
367 self.graph.type_name(self.root)
368 }
369
370 fn container(items: impl IntoIterator<Item = Self>, make: impl FnOnce(Vec<NodeId>) -> MontyNode) -> Self {
372 let mut graph = MontyGraph::new();
373 let ids = items.into_iter().map(|item| item.push_into(&mut graph)).collect();
374 let root = graph.push(make(ids));
375 Self { graph, root }
376 }
377}
378
379impl PartialEq for MontyObject {
380 fn eq(&self, other: &Self) -> bool {
382 self.as_ref() == other.as_ref()
383 }
384}
385
386impl PartialEq<ObjectRef<'_>> for MontyObject {
387 fn eq(&self, other: &ObjectRef<'_>) -> bool {
388 self.as_ref() == *other
389 }
390}
391
392impl fmt::Display for MontyObject {
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 self.as_ref().fmt(f)
396 }
397}
398
399impl TryFrom<&MontyObject> for i64 {
400 type Error = ConversionError;
401
402 fn try_from(value: &MontyObject) -> Result<Self, ConversionError> {
403 value.as_ref().try_into()
404 }
405}
406
407impl TryFrom<&MontyObject> for f64 {
408 type Error = ConversionError;
409
410 fn try_from(value: &MontyObject) -> Result<Self, ConversionError> {
411 value.as_ref().try_into()
412 }
413}
414
415impl TryFrom<&MontyObject> for String {
416 type Error = ConversionError;
417
418 fn try_from(value: &MontyObject) -> Result<Self, ConversionError> {
419 value.as_ref().try_into()
420 }
421}
422
423impl TryFrom<&MontyObject> for bool {
424 type Error = ConversionError;
425
426 fn try_from(value: &MontyObject) -> Result<Self, ConversionError> {
427 value.as_ref().try_into()
428 }
429}
430
431#[derive(Debug, Clone, Copy)]
433pub struct ObjectRef<'a> {
434 graph: &'a MontyGraph,
436 id: NodeId,
438}
439
440impl<'a> ObjectRef<'a> {
441 #[must_use]
443 fn node(&self) -> &'a MontyNode {
444 self.graph.node(self.id)
445 }
446
447 #[must_use]
449 pub fn type_name(&self) -> &'a str {
450 self.graph.type_name(self.id)
451 }
452
453 #[must_use]
455 pub fn to_owned(&self) -> MontyObject {
456 let mut graph = MontyGraph::new();
457 let root = self.push_into(&mut graph);
458 MontyObject { graph, root }
459 }
460
461 #[must_use]
463 fn child(&self, id: NodeId) -> Self {
464 self.graph.value(id)
465 }
466
467 #[must_use]
470 pub fn items(&self) -> Option<Vec<Self>> {
471 match self.node() {
472 MontyNode::List(ids)
473 | MontyNode::Tuple(ids)
474 | MontyNode::Set(ids)
475 | MontyNode::FrozenSet(ids)
476 | MontyNode::NamedTuple { values: ids, .. } => Some(ids.iter().map(|id| self.child(*id)).collect()),
477 _ => None,
478 }
479 }
480
481 #[must_use]
484 pub fn pairs(&self) -> Option<Vec<(Self, Self)>> {
485 let pairs = match self.node() {
486 MontyNode::Dict(pairs)
487 | MontyNode::ClassInstance { attrs: pairs, .. }
488 | MontyNode::Module { attrs: pairs, .. } => pairs,
489 MontyNode::ClassType(class) => &class.attrs,
490 _ => return None,
491 };
492 Some(
493 pairs
494 .iter()
495 .map(|(key, value)| (self.child(*key), self.child(*value)))
496 .collect(),
497 )
498 }
499
500 #[must_use]
502 pub fn as_int(&self) -> Option<i64> {
503 match self.node() {
504 MontyNode::Int(value) => Some(*value),
505 MontyNode::BigInt(value) => value.to_i64(),
506 _ => None,
507 }
508 }
509
510 #[must_use]
512 pub fn as_str(&self) -> Option<&'a str> {
513 match self.node() {
514 MontyNode::String(value) => Some(value),
515 _ => None,
516 }
517 }
518
519 #[must_use]
521 pub fn as_bool(&self) -> Option<bool> {
522 match self.node() {
523 MontyNode::Bool(value) => Some(*value),
524 _ => None,
525 }
526 }
527
528 #[must_use]
531 pub fn as_float(&self) -> Option<f64> {
532 match self.node() {
533 MontyNode::Float(value) => Some(*value),
534 MontyNode::Int(value) => Some(*value as f64),
535 MontyNode::BigInt(value) => value.to_f64().filter(|f| f.is_finite()),
536 _ => None,
537 }
538 }
539
540 #[must_use]
542 pub fn as_decimal(&self) -> Option<&str> {
543 match self.node() {
544 MontyNode::Decimal(value) => Some(value),
545 _ => None,
546 }
547 }
548
549 #[must_use]
553 pub fn as_complex(&self) -> Option<MontyComplex> {
554 match self.node() {
555 MontyNode::Complex(value) => Some(*value),
556 _ => self.as_float().map(|real| MontyComplex { real, imag: 0.0 }),
557 }
558 }
559
560 #[must_use]
565 pub fn py_repr(&self) -> String {
566 let mut s = String::new();
567 self.repr_fmt(&mut s).expect("Unable to format repr display value");
568 s
569 }
570
571 #[must_use]
574 pub fn is_truthy(&self) -> bool {
575 match self.node() {
576 MontyNode::None => false,
577 MontyNode::Bool(b) => *b,
578 MontyNode::Int(i) => *i != 0,
579 MontyNode::BigInt(bi) => !bi.is_zero(),
580 MontyNode::Float(f) => *f != 0.0,
581 MontyNode::Complex(c) => c.real != 0.0 || c.imag != 0.0,
582 MontyNode::String(s) => !s.is_empty(),
583 MontyNode::Bytes(b) => !b.is_empty(),
584 MontyNode::List(items)
585 | MontyNode::Tuple(items)
586 | MontyNode::Set(items)
587 | MontyNode::FrozenSet(items)
588 | MontyNode::NamedTuple { values: items, .. } => !items.is_empty(),
589 MontyNode::Dict(pairs) => !pairs.is_empty(),
590 MontyNode::TimeDelta(delta) => delta.days != 0 || delta.seconds != 0 || delta.microseconds != 0,
591 MontyNode::Decimal(text) => !decimal_is_zero(text),
592 _ => true,
593 }
594 }
595
596 fn repr_fmt(&self, f: &mut impl Write) -> fmt::Result {
601 let mut stack: Vec<vec::IntoIter<ReprPiece<'a>>> = Vec::new();
602 let mut pending = Some(self.id);
603 loop {
604 if let Some(id) = pending.take() {
605 let value = self.child(id);
606 match value.repr_pieces() {
607 Some(pieces) => stack.push(pieces.into_iter()),
608 None => value.leaf_repr_fmt(f)?,
609 }
610 }
611 let Some(pieces) = stack.last_mut() else {
612 return Ok(());
613 };
614 match pieces.next() {
615 Some(ReprPiece::Text(text)) => f.write_str(text)?,
616 Some(ReprPiece::Child(child)) => pending = Some(child),
617 None => {
618 stack.pop();
619 }
620 }
621 }
622 }
623
624 fn repr_pieces(&self) -> Option<Vec<ReprPiece<'a>>> {
626 let mut pieces = Vec::new();
627 match self.node() {
628 MontyNode::List(ids) => {
629 pieces.push(Text("["));
630 push_repr_items(&mut pieces, ids);
631 pieces.push(Text("]"));
632 }
633 MontyNode::Tuple(ids) => {
634 pieces.push(Text("("));
635 push_repr_items(&mut pieces, ids);
636 pieces.push(Text(")"));
637 }
638 MontyNode::NamedTuple {
639 type_name,
640 field_names,
641 values,
642 } => {
643 pieces.extend([Text(type_name), Text("(")]);
645 for (i, (name, id)) in field_names.iter().zip(values).enumerate() {
646 push_repr_separator(&mut pieces, i);
647 pieces.extend([Text(name), Text("="), Child(*id)]);
648 }
649 pieces.push(Text(")"));
650 }
651 MontyNode::Dict(pairs) => {
652 pieces.push(Text("{"));
653 for (i, (key, value)) in pairs.iter().enumerate() {
654 push_repr_separator(&mut pieces, i);
655 pieces.extend([Child(*key), Text(": "), Child(*value)]);
656 }
657 pieces.push(Text("}"));
658 }
659 MontyNode::Set(ids) if ids.is_empty() => pieces.push(Text("set()")),
660 MontyNode::Set(ids) => {
661 pieces.push(Text("{"));
662 push_repr_items(&mut pieces, ids);
663 pieces.push(Text("}"));
664 }
665 MontyNode::FrozenSet(ids) => {
666 pieces.push(Text("frozenset("));
667 if !ids.is_empty() {
668 pieces.push(Text("{"));
669 push_repr_items(&mut pieces, ids);
670 pieces.push(Text("}"));
671 }
672 pieces.push(Text(")"));
673 }
674 MontyNode::ClassInstance { attrs, .. } => {
675 pieces.extend([Text(self.type_name()), Text("(")]);
678 for (i, (key, value)) in attrs.iter().enumerate() {
679 push_repr_separator(&mut pieces, i);
680 match self.graph.node(*key) {
681 MontyNode::String(key) => pieces.push(Text(key)),
682 _ => pieces.push(Child(*key)),
683 }
684 pieces.extend([Text("="), Child(*value)]);
685 }
686 pieces.push(Text(")"));
687 }
688 _ => return None,
689 }
690 Some(pieces)
691 }
692
693 fn leaf_repr_fmt(&self, f: &mut impl Write) -> fmt::Result {
695 match self.node() {
696 MontyNode::Ellipsis => f.write_str("Ellipsis"),
697 MontyNode::NotImplemented => f.write_str("NotImplemented"),
698 MontyNode::None => f.write_str("None"),
699 MontyNode::Bool(true) => f.write_str("True"),
700 MontyNode::Bool(false) => f.write_str("False"),
701 MontyNode::Int(v) => write!(f, "{v}"),
702 MontyNode::BigInt(v) => write!(f, "{v}"),
703 MontyNode::Float(v) => write!(f, "{}", FormatFloat(*v)),
704 MontyNode::Complex(c) => write!(
705 f,
706 "{}",
707 FormatComplex {
708 real: c.real,
709 imag: c.imag
710 }
711 ),
712 MontyNode::String(s) => string_repr_fmt(s, f),
713 MontyNode::Bytes(b) => bytes_repr_fmt(b, f),
714 MontyNode::Date(date) => write!(f, "datetime.date({}, {}, {})", date.year, date.month, date.day),
715 MontyNode::DateTime(datetime) => {
716 write!(
717 f,
718 "datetime.datetime({}, {}, {}, {}, {}",
719 datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute
720 )?;
721 if datetime.second != 0 || datetime.microsecond != 0 {
722 write!(f, ", {}", datetime.second)?;
723 }
724 if datetime.microsecond != 0 {
725 write!(f, ", {}", datetime.microsecond)?;
726 }
727 if let Some(offset) = datetime.offset_seconds {
728 tzinfo_repr_fmt(f, offset, datetime.timezone_name.as_deref())?;
729 }
730 f.write_char(')')
731 }
732 MontyNode::Time(time) => {
733 write!(f, "datetime.time({}, {}", time.hour, time.minute)?;
734 if time.second != 0 || time.microsecond != 0 {
737 write!(f, ", {}", time.second)?;
738 }
739 if time.microsecond != 0 {
740 write!(f, ", {}", time.microsecond)?;
741 }
742 if let Some(offset) = time.offset_seconds {
743 tzinfo_repr_fmt(f, offset, time.timezone_name.as_deref())?;
744 }
745 if time.fold != 0 {
746 write!(f, ", fold={}", time.fold)?;
747 }
748 f.write_char(')')
749 }
750 MontyNode::TimeDelta(delta) => {
751 if delta.days == 0 && delta.seconds == 0 && delta.microseconds == 0 {
752 return f.write_str("datetime.timedelta(0)");
753 }
754 f.write_str("datetime.timedelta(")?;
755 let mut first = true;
756 if delta.days != 0 {
757 write!(f, "days={}", delta.days)?;
758 first = false;
759 }
760 if delta.seconds != 0 {
761 if !first {
762 f.write_str(", ")?;
763 }
764 write!(f, "seconds={}", delta.seconds)?;
765 first = false;
766 }
767 if delta.microseconds != 0 {
768 if !first {
769 f.write_str(", ")?;
770 }
771 write!(f, "microseconds={}", delta.microseconds)?;
772 }
773 f.write_char(')')
774 }
775 MontyNode::Decimal(text) => write!(f, "Decimal('{text}')"),
776 MontyNode::TimeZone(tz) => {
777 if tz.offset_seconds == 0 && tz.name.is_none() {
778 return f.write_str("datetime.timezone.utc");
779 }
780 let timedelta_repr = format_offset_timedelta_repr(tz.offset_seconds);
781 write!(f, "datetime.timezone({timedelta_repr}")?;
782 if let Some(name) = &tz.name {
783 write!(f, ", {}", StringRepr(name))?;
784 }
785 f.write_char(')')
786 }
787 MontyNode::Exception { exc_type, arg } => {
788 let type_str: &'static str = exc_type.into();
789 write!(f, "{type_str}(")?;
790 if let Some(arg) = arg {
791 string_repr_fmt(arg, f)?;
792 }
793 f.write_char(')')
794 }
795 MontyNode::Path(p) => write!(f, "PosixPath('{p}')"),
796 MontyNode::FileHandle(handle) => write!(f, "{handle}"),
797 MontyNode::Type(t) => write!(f, "<class '{t}'>"),
798 MontyNode::ClassType(class) => write!(f, "<class '{}'>", class.name),
799 MontyNode::BuiltinFunction(func) => write!(f, "<built-in function {func}>"),
800 MontyNode::Function { name, .. } => write!(f, "<function '{name}' external>"),
801 MontyNode::Module { name, .. } => write!(f, "<module {}>", StringRepr(name)),
803 MontyNode::Repr(s) => write!(f, "Repr({})", StringRepr(s)),
804 MontyNode::Cycle(placeholder) => f.write_str(placeholder),
805 MontyNode::List(_)
806 | MontyNode::Tuple(_)
807 | MontyNode::NamedTuple { .. }
808 | MontyNode::Dict(_)
809 | MontyNode::Set(_)
810 | MontyNode::FrozenSet(_)
811 | MontyNode::ClassInstance { .. } => unreachable!("containers render through repr_pieces"),
812 }
813 }
814}
815
816enum ReprPiece<'a> {
819 Text(&'a str),
820 Child(NodeId),
821}
822
823fn push_repr_items(pieces: &mut Vec<ReprPiece<'_>>, ids: &[NodeId]) {
825 for (i, id) in ids.iter().enumerate() {
826 push_repr_separator(pieces, i);
827 pieces.push(ReprPiece::Child(*id));
828 }
829}
830
831fn push_repr_separator(pieces: &mut Vec<ReprPiece<'_>>, index: usize) {
833 if index > 0 {
834 pieces.push(ReprPiece::Text(", "));
835 }
836}
837
838impl PartialEq for ObjectRef<'_> {
839 fn eq(&self, other: &Self) -> bool {
845 let mut pending = vec![(self.id, other.id)];
846 let mut seen = HashSet::new();
847 while let Some((a, b)) = pending.pop() {
848 if !seen.insert((a, b)) {
849 continue;
850 }
851 if !nodes_eq(self.graph.node(a), other.graph.node(b), &mut pending) {
852 return false;
853 }
854 }
855 true
856 }
857}
858
859impl PartialEq<MontyObject> for ObjectRef<'_> {
860 fn eq(&self, other: &MontyObject) -> bool {
861 *self == other.as_ref()
862 }
863}
864
865impl fmt::Display for ObjectRef<'_> {
866 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868 match self.node() {
869 MontyNode::String(s) | MontyNode::Cycle(s) => f.write_str(s),
870 _ => self.repr_fmt(f),
871 }
872 }
873}
874
875impl TryFrom<ObjectRef<'_>> for i64 {
876 type Error = ConversionError;
877
878 fn try_from(value: ObjectRef<'_>) -> Result<Self, ConversionError> {
879 match value.node() {
880 MontyNode::Int(i) => Ok(*i),
881 _ => Err(ConversionError::new("int", value.type_name())),
882 }
883 }
884}
885
886impl TryFrom<ObjectRef<'_>> for f64 {
888 type Error = ConversionError;
889
890 fn try_from(value: ObjectRef<'_>) -> Result<Self, ConversionError> {
891 value
892 .as_float()
893 .ok_or_else(|| ConversionError::new("float", value.type_name()))
894 }
895}
896
897impl TryFrom<ObjectRef<'_>> for String {
898 type Error = ConversionError;
899
900 fn try_from(value: ObjectRef<'_>) -> Result<Self, ConversionError> {
901 value
902 .as_str()
903 .map(str::to_owned)
904 .ok_or_else(|| ConversionError::new("str", value.type_name()))
905 }
906}
907
908impl TryFrom<ObjectRef<'_>> for bool {
910 type Error = ConversionError;
911
912 fn try_from(value: ObjectRef<'_>) -> Result<Self, ConversionError> {
913 value
914 .as_bool()
915 .ok_or_else(|| ConversionError::new("bool", value.type_name()))
916 }
917}
918
919fn tzinfo_repr_fmt(f: &mut impl Write, offset: i32, name: Option<&str>) -> fmt::Result {
921 if offset == 0 && name.is_none() {
922 f.write_str(", tzinfo=datetime.timezone.utc")
923 } else {
924 let timedelta_repr = format_offset_timedelta_repr(offset);
925 write!(f, ", tzinfo=datetime.timezone({timedelta_repr}")?;
926 if let Some(name) = name {
927 write!(f, ", {}", StringRepr(name))?;
928 }
929 f.write_char(')')
930 }
931}
932
933fn nodes_eq(a: &MontyNode, b: &MontyNode, pending: &mut Vec<(NodeId, NodeId)>) -> bool {
936 match (a, b) {
937 (MontyNode::Int(x), MontyNode::BigInt(y)) | (MontyNode::BigInt(y), MontyNode::Int(x)) => y.to_i64() == Some(*x),
939 (MontyNode::NamedTuple { values: xs, .. }, MontyNode::Tuple(ys))
941 | (MontyNode::Tuple(xs), MontyNode::NamedTuple { values: ys, .. }) => queue_items(xs, ys, pending),
942 (MontyNode::List(xs), MontyNode::List(ys))
943 | (MontyNode::Tuple(xs), MontyNode::Tuple(ys))
944 | (MontyNode::Set(xs), MontyNode::Set(ys))
945 | (MontyNode::FrozenSet(xs), MontyNode::FrozenSet(ys)) => queue_items(xs, ys, pending),
946 (
947 MontyNode::NamedTuple {
948 type_name: x_type,
949 field_names: x_fields,
950 values: xs,
951 },
952 MontyNode::NamedTuple {
953 type_name: y_type,
954 field_names: y_fields,
955 values: ys,
956 },
957 ) => x_type == y_type && x_fields == y_fields && queue_items(xs, ys, pending),
958 (MontyNode::Dict(xs), MontyNode::Dict(ys)) => queue_pairs(xs, ys, pending),
959 (MontyNode::ClassType(x), MontyNode::ClassType(y)) => {
960 x.name == y.name
961 && x.id == y.id
962 && x.host_defined == y.host_defined
963 && x.is_dataclass == y.is_dataclass
964 && queue_pairs(&x.attrs, &y.attrs, pending)
965 }
966 (
967 MontyNode::ClassInstance {
968 class_type: x_class,
969 instance_id: x_id,
970 attrs: xs,
971 },
972 MontyNode::ClassInstance {
973 class_type: y_class,
974 instance_id: y_id,
975 attrs: ys,
976 },
977 ) => {
978 x_id == y_id && {
979 pending.push((*x_class, *y_class));
980 queue_pairs(xs, ys, pending)
981 }
982 }
983 (
984 MontyNode::Module {
985 name: x_name,
986 id: x_id,
987 attrs: xs,
988 },
989 MontyNode::Module {
990 name: y_name,
991 id: y_id,
992 attrs: ys,
993 },
994 ) => x_name == y_name && x_id == y_id && queue_pairs(xs, ys, pending),
995 _ => a.is_leaf() && b.is_leaf() && a == b,
997 }
998}
999
1000fn queue_items(xs: &[NodeId], ys: &[NodeId], pending: &mut Vec<(NodeId, NodeId)>) -> bool {
1002 xs.len() == ys.len() && {
1003 pending.extend(xs.iter().copied().zip(ys.iter().copied()));
1004 true
1005 }
1006}
1007
1008fn queue_pairs(xs: &[(NodeId, NodeId)], ys: &[(NodeId, NodeId)], pending: &mut Vec<(NodeId, NodeId)>) -> bool {
1011 xs.len() == ys.len() && {
1012 for ((xk, xv), (yk, yv)) in xs.iter().zip(ys) {
1013 pending.push((*xk, *yk));
1014 pending.push((*xv, *yv));
1015 }
1016 true
1017 }
1018}
1019
1020#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1023pub struct CallArgs {
1024 graph: MontyGraph,
1026 arg_ids: Vec<NodeId>,
1028 kwarg_ids: Vec<(NodeId, NodeId)>,
1030}
1031
1032impl CallArgs {
1033 #[must_use]
1035 pub fn new() -> Self {
1036 Self::default()
1037 }
1038
1039 pub fn push_arg(&mut self, value: MontyObject) {
1041 unstable::push_arg(self, value);
1042 }
1043
1044 pub fn push_kwarg(&mut self, name: &str, value: MontyObject) {
1046 unstable::push_kwarg(self, name, value);
1047 }
1048
1049 #[must_use]
1051 pub fn arg(&self, index: usize) -> Option<ObjectRef<'_>> {
1052 self.arg_ids.get(index).map(|id| self.graph.value(*id))
1053 }
1054
1055 #[must_use = "iterators are lazy and do nothing unless consumed"]
1057 pub fn args(&self) -> impl ExactSizeIterator<Item = ObjectRef<'_>> {
1058 self.arg_ids.iter().map(|id| self.graph.value(*id))
1059 }
1060
1061 #[must_use = "iterators are lazy and do nothing unless consumed"]
1063 pub fn kwargs(&self) -> impl ExactSizeIterator<Item = (ObjectRef<'_>, ObjectRef<'_>)> {
1064 self.kwarg_ids
1065 .iter()
1066 .map(|(key, value)| (self.graph.value(*key), self.graph.value(*value)))
1067 }
1068
1069 #[must_use]
1071 pub fn kwarg(&self, name: &str) -> Option<ObjectRef<'_>> {
1072 self.kwargs()
1073 .find(|(key, _)| key.as_str() == Some(name))
1074 .map(|(_, value)| value)
1075 }
1076
1077 fn check_roots(&self) -> Result<(), GraphError> {
1079 self.arg_ids.iter().try_for_each(|id| self.graph.check_root(*id))?;
1080 self.kwarg_ids
1081 .iter()
1082 .try_for_each(|(key, value)| self.graph.check_root(*key).and_then(|()| self.graph.check_root(*value)))
1083 }
1084}
1085
1086impl From<Vec<MontyObject>> for CallArgs {
1088 fn from(args: Vec<MontyObject>) -> Self {
1089 Self::from((args, Vec::new()))
1090 }
1091}
1092
1093impl From<(Vec<MontyObject>, Vec<(MontyObject, MontyObject)>)> for CallArgs {
1095 fn from((args, kwargs): (Vec<MontyObject>, Vec<(MontyObject, MontyObject)>)) -> Self {
1096 let mut call = Self::new();
1097 for arg in args {
1098 call.push_arg(arg);
1099 }
1100 call.kwarg_ids = push_pairs(kwargs, &mut call.graph);
1101 call
1102 }
1103}
1104
1105#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1107pub struct NamedValues {
1108 graph: MontyGraph,
1110 names: Vec<(String, NodeId)>,
1112}
1113
1114impl NamedValues {
1115 #[must_use]
1117 pub fn new() -> Self {
1118 Self::default()
1119 }
1120
1121 pub fn push(&mut self, name: impl Into<String>, value: MontyObject) {
1123 unstable::push_named(self, name, value);
1124 }
1125
1126 #[must_use]
1128 pub fn len(&self) -> usize {
1129 self.names.len()
1130 }
1131
1132 #[must_use]
1134 pub fn is_empty(&self) -> bool {
1135 self.names.is_empty()
1136 }
1137
1138 #[must_use = "iterators are lazy and do nothing unless consumed"]
1140 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, ObjectRef<'_>)> {
1141 self.names
1142 .iter()
1143 .map(|(name, id)| (name.as_str(), self.graph.value(*id)))
1144 }
1145
1146 fn check_roots(&self) -> Result<(), GraphError> {
1148 self.names.iter().try_for_each(|(_, id)| self.graph.check_root(*id))
1149 }
1150}
1151
1152impl From<Vec<(String, MontyObject)>> for NamedValues {
1154 fn from(pairs: Vec<(String, MontyObject)>) -> Self {
1155 let mut named = Self::new();
1156 for (name, value) in pairs {
1157 named.push(name, value);
1158 }
1159 named
1160 }
1161}
1162
1163impl MontyGraph {
1164 #[must_use]
1169 pub fn value(&self, id: NodeId) -> ObjectRef<'_> {
1170 assert!(id.index() < self.len(), "node id {id} is out of range");
1171 ObjectRef { graph: self, id }
1172 }
1173}
1174
1175impl PushValue for MontyObject {
1176 fn push_into(self, graph: &mut MontyGraph) -> NodeId {
1178 let offset = graph.merge(self.graph);
1179 NodeId(self.root.0 + offset)
1180 }
1181}
1182
1183impl PushValue for ObjectRef<'_> {
1184 fn push_into(self, graph: &mut MontyGraph) -> NodeId {
1188 let nodes = self.graph.nodes();
1189 let root = self.id.index();
1190 let mut lowest = root;
1194 let mut index = root;
1195 loop {
1196 nodes[index].for_each_child(|child| lowest = lowest.min(child.index()));
1197 if index == lowest {
1198 break;
1199 }
1200 index -= 1;
1201 }
1202 let span = &nodes[lowest..=root];
1203 let mut reachable = vec![false; span.len()];
1205 reachable[root - lowest] = true;
1206 for (offset, node) in span.iter().enumerate().rev() {
1207 if reachable[offset] {
1208 node.for_each_child(|child| reachable[child.index() - lowest] = true);
1209 }
1210 }
1211 let mut copied: Vec<Option<NodeId>> = vec![None; span.len()];
1213 for (offset, node) in span.iter().enumerate() {
1214 if reachable[offset] {
1215 let mut node = node.clone();
1216 node.for_each_child_mut(|child| {
1217 *child = copied[child.index() - lowest].expect("children are copied first");
1218 });
1219 copied[offset] = Some(graph.push(node));
1220 }
1221 }
1222 copied[root - lowest].expect("the root is copied")
1223 }
1224}
1225
1226fn push_pairs(
1228 pairs: impl IntoIterator<Item = (MontyObject, MontyObject)>,
1229 graph: &mut MontyGraph,
1230) -> Vec<(NodeId, NodeId)> {
1231 pairs
1232 .into_iter()
1233 .map(|(key, value)| {
1234 let key = key.push_into(graph);
1235 let value = value.push_into(graph);
1236 (key, value)
1237 })
1238 .collect()
1239}
1240
1241#[derive(
1250 Debug,
1251 Clone,
1252 Copy,
1253 PartialEq,
1254 Eq,
1255 serde::Serialize,
1256 serde::Deserialize,
1257 strum::EnumIter,
1258 strum::EnumString,
1259 strum::IntoStaticStr,
1260 strum::VariantNames,
1261)]
1262#[strum(serialize_all = "lowercase")]
1263pub enum MontyType {
1264 Ellipsis,
1265 Type,
1266 #[strum(serialize = "NoneType")]
1267 NoneType,
1268 Bool,
1269 Int,
1270 Float,
1271 Complex,
1272 Range,
1273 Slice,
1274 #[strum(serialize = "datetime.date")]
1278 Date,
1279 #[strum(serialize = "datetime.datetime")]
1280 DateTime,
1281 #[strum(serialize = "datetime.timedelta")]
1282 TimeDelta,
1283 #[strum(serialize = "datetime.timezone")]
1284 TimeZone,
1285 Str,
1286 Bytes,
1287 List,
1288 #[strum(serialize = "collections.deque")]
1292 Deque,
1293 #[strum(serialize = "list_iterator")]
1294 ListIterator,
1295 #[strum(serialize = "callable_iterator")]
1296 CallableIterator,
1297 Tuple,
1298 NamedTuple,
1299 Dict,
1300 #[strum(serialize = "dict_keys")]
1301 DictKeys,
1302 #[strum(serialize = "dict_items")]
1303 DictItems,
1304 #[strum(serialize = "dict_values")]
1305 DictValues,
1306 Set,
1307 FrozenSet,
1308 #[strum(disabled)]
1314 Exception(ExcType),
1315 Function,
1316 #[strum(serialize = "builtin_function_or_method")]
1317 BuiltinFunction,
1318 Cell,
1319 Iterator,
1320 Coroutine,
1321 Module,
1322 #[strum(serialize = "_io.TextIOWrapper")]
1323 TextIOWrapper,
1324 #[strum(serialize = "_io.BufferedReader")]
1325 BufferedReader,
1326 #[strum(serialize = "_io.BufferedWriter")]
1327 BufferedWriter,
1328 #[strum(serialize = "_io.BufferedRandom")]
1329 BufferedRandom,
1330 #[strum(serialize = "typing._SpecialForm")]
1331 SpecialForm,
1332 #[strum(serialize = "PosixPath")]
1333 Path,
1334 Property,
1335 #[strum(serialize = "re.Pattern")]
1336 RePattern,
1337 #[strum(serialize = "re.Match")]
1338 ReMatch,
1339 #[strum(serialize = "tuple_iterator")]
1341 TupleIterator,
1342 #[strum(serialize = "str_ascii_iterator")]
1343 StrAsciiIterator,
1344 #[strum(serialize = "str_iterator")]
1345 StrIterator,
1346 #[strum(serialize = "bytes_iterator")]
1347 BytesIterator,
1348 #[strum(serialize = "range_iterator")]
1349 RangeIterator,
1350 #[strum(serialize = "dict_keyiterator")]
1351 DictKeyIterator,
1352 #[strum(serialize = "dict_itemiterator")]
1353 DictItemIterator,
1354 #[strum(serialize = "dict_valueiterator")]
1355 DictValueIterator,
1356 #[strum(serialize = "set_iterator")]
1357 SetIterator,
1358 #[strum(serialize = "itertools.count")]
1359 ItertoolsCount,
1360 #[strum(serialize = "itertools.repeat")]
1361 ItertoolsRepeat,
1362 #[strum(serialize = "Field")]
1365 Field,
1366 #[strum(serialize = "itertools.pairwise")]
1367 ItertoolsPairwise,
1368 #[strum(serialize = "itertools.compress")]
1369 ItertoolsCompress,
1370 #[strum(serialize = "itertools.islice")]
1371 ItertoolsIslice,
1372 #[strum(serialize = "itertools.chain")]
1373 ItertoolsChain,
1374 #[strum(serialize = "itertools.cycle")]
1375 ItertoolsCycle,
1376 #[strum(serialize = "NotImplementedType")]
1377 NotImplementedType,
1378 #[strum(serialize = "_DataclassParams")]
1381 DataclassParams,
1382 #[strum(serialize = "itertools.takewhile")]
1383 ItertoolsTakeWhile,
1384 #[strum(serialize = "itertools.dropwhile")]
1385 ItertoolsDropWhile,
1386 #[strum(serialize = "itertools.filterfalse")]
1387 ItertoolsFilterFalse,
1388 #[strum(serialize = "itertools.starmap")]
1389 ItertoolsStarMap,
1390 Object,
1393 #[strum(serialize = "datetime.time")]
1394 Time,
1395 #[strum(serialize = "functools.partial")]
1397 Partial,
1398 #[strum(serialize = "itertools.accumulate")]
1399 ItertoolsAccumulate,
1400 #[strum(serialize = "itertools.batched")]
1401 ItertoolsBatched,
1402 #[strum(serialize = "itertools.zip_longest")]
1403 ItertoolsZipLongest,
1404 #[strum(serialize = "types.GenericAlias")]
1406 GenericAlias,
1407 #[strum(serialize = "typing.Union")]
1409 Union,
1410 #[strum(serialize = "itertools.combinations")]
1411 ItertoolsCombinations,
1412 #[strum(serialize = "itertools.combinations_with_replacement")]
1413 ItertoolsCombinationsWithReplacement,
1414 #[strum(serialize = "itertools.permutations")]
1415 ItertoolsPermutations,
1416 #[strum(serialize = "itertools.product")]
1417 ItertoolsProduct,
1418 #[strum(serialize = "itertools.groupby")]
1419 ItertoolsGroupBy,
1420 #[strum(serialize = "itertools._grouper")]
1421 ItertoolsGrouper,
1422 #[strum(serialize = "itertools._tee")]
1423 ItertoolsTee,
1424 #[strum(serialize = "itertools._tee_dataobject")]
1425 ItertoolsTeeDataObject,
1426 #[strum(serialize = "functools._lru_cache_wrapper")]
1428 LruCacheWrapper,
1429 #[strum(serialize = "decimal.Decimal")]
1431 Decimal,
1432}
1433
1434impl fmt::Display for MontyType {
1435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1436 f.write_str(self.name())
1437 }
1438}
1439
1440impl MontyType {
1441 #[must_use]
1444 pub fn name(&self) -> &str {
1445 match self {
1446 Self::Exception(exc_type) => (*exc_type).into(),
1447 other => other.into(),
1450 }
1451 }
1452
1453 #[must_use]
1456 pub fn from_type_name(name: &str) -> Option<Self> {
1457 name.parse::<Self>()
1458 .ok()
1459 .or_else(|| name.parse::<ExcType>().ok().map(Self::Exception))
1460 }
1461}
1462
1463#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1465pub struct MontyDate {
1466 pub year: i32,
1468 pub month: u8,
1470 pub day: u8,
1472}
1473
1474#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1476pub struct MontyDateTime {
1477 pub year: i32,
1479 pub month: u8,
1481 pub day: u8,
1483 pub hour: u8,
1485 pub minute: u8,
1487 pub second: u8,
1489 pub microsecond: u32,
1491 pub offset_seconds: Option<i32>,
1495 pub timezone_name: Option<String>,
1499}
1500
1501#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1506pub struct MontyTime {
1507 pub hour: u8,
1509 pub minute: u8,
1511 pub second: u8,
1513 pub microsecond: u32,
1515 pub offset_seconds: Option<i32>,
1519 pub timezone_name: Option<String>,
1523 pub fold: u8,
1525}
1526
1527#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
1532pub struct MontyComplex {
1533 pub real: f64,
1535 pub imag: f64,
1537}
1538
1539impl MontyComplex {
1540 #[must_use]
1542 pub fn bits_eq(&self, other: &Self) -> bool {
1543 self.real.to_bits() == other.real.to_bits() && self.imag.to_bits() == other.imag.to_bits()
1544 }
1545}
1546
1547#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1549pub struct MontyTimeDelta {
1550 pub days: i32,
1552 pub seconds: i32,
1554 pub microseconds: i32,
1556}
1557
1558pub const MIN_TIMEZONE_OFFSET_SECONDS: i32 = -86_399;
1564pub const MAX_TIMEZONE_OFFSET_SECONDS: i32 = 86_399;
1568
1569#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1571pub struct MontyTimeZone {
1572 pub offset_seconds: i32,
1574 pub name: Option<String>,
1576}
1577
1578fn monty_time_local_micros(time: &MontyTime) -> i64 {
1583 i64::from(time.hour) * 3_600_000_000
1584 + i64::from(time.minute) * 60_000_000
1585 + i64::from(time.second) * 1_000_000
1586 + i64::from(time.microsecond)
1587}
1588
1589fn monty_time_key(time: &MontyTime) -> i64 {
1596 monty_time_local_micros(time) - i64::from(time.offset_seconds.unwrap_or(0)) * 1_000_000
1597}
1598
1599impl PartialEq for MontyTime {
1602 fn eq(&self, other: &Self) -> bool {
1603 self.offset_seconds.is_some() == other.offset_seconds.is_some() && monty_time_key(self) == monty_time_key(other)
1604 }
1605}
1606
1607impl Eq for MontyTime {}
1608
1609impl Hash for MontyTime {
1610 fn hash<H: Hasher>(&self, state: &mut H) {
1611 self.offset_seconds.is_some().hash(state);
1614 monty_time_key(self).hash(state);
1615 }
1616}
1617
1618impl PartialEq for MontyDateTime {
1619 fn eq(&self, other: &Self) -> bool {
1620 let self_aware = self.offset_seconds.is_some();
1621 let other_aware = other.offset_seconds.is_some();
1622 if self_aware != other_aware {
1623 return false;
1624 }
1625
1626 if self_aware {
1627 return monty_datetime_utc_micros(self)
1628 .zip(monty_datetime_utc_micros(other))
1629 .is_some_and(|(lhs, rhs)| lhs == rhs)
1630 || monty_datetime_raw_eq(self, other);
1631 }
1632
1633 monty_datetime_local_micros(self)
1634 .zip(monty_datetime_local_micros(other))
1635 .is_some_and(|(lhs, rhs)| lhs == rhs)
1636 || monty_datetime_raw_eq(self, other)
1637 }
1638}
1639
1640impl Eq for MontyDateTime {}
1641
1642impl Hash for MontyDateTime {
1643 fn hash<H: Hasher>(&self, state: &mut H) {
1644 if self.offset_seconds.is_some()
1645 && let Some(utc_micros) = monty_datetime_utc_micros(self)
1646 {
1647 utc_micros.hash(state);
1648 return;
1649 }
1650 if let Some(local_micros) = monty_datetime_local_micros(self) {
1651 local_micros.hash(state);
1652 return;
1653 }
1654
1655 self.year.hash(state);
1657 self.month.hash(state);
1658 self.day.hash(state);
1659 self.hour.hash(state);
1660 self.minute.hash(state);
1661 self.second.hash(state);
1662 self.microsecond.hash(state);
1663 self.offset_seconds.hash(state);
1664 self.timezone_name.hash(state);
1665 }
1666}
1667
1668impl PartialEq for MontyTimeZone {
1669 fn eq(&self, other: &Self) -> bool {
1670 self.offset_seconds == other.offset_seconds
1671 }
1672}
1673
1674impl Eq for MontyTimeZone {}
1675
1676impl Hash for MontyTimeZone {
1677 fn hash<H: Hasher>(&self, state: &mut H) {
1678 self.offset_seconds.hash(state);
1679 }
1680}
1681
1682#[derive(Debug)]
1687pub struct ConversionError {
1688 pub expected: &'static str,
1690 pub actual: String,
1693}
1694
1695impl ConversionError {
1696 #[must_use]
1698 pub fn new(expected: &'static str, actual: impl Into<String>) -> Self {
1699 Self {
1700 expected,
1701 actual: actual.into(),
1702 }
1703 }
1704}
1705
1706impl fmt::Display for ConversionError {
1707 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1708 write!(f, "expected {}, got {}", self.expected, self.actual)
1709 }
1710}
1711
1712impl Error for ConversionError {}
1713
1714#[derive(Debug, Clone)]
1720pub enum InvalidInputError {
1721 InvalidType(Cow<'static, str>),
1724 Resource(ResourceError),
1726}
1727
1728impl InvalidInputError {
1729 #[must_use]
1731 pub fn invalid_type(msg: impl Into<Cow<'static, str>>) -> Self {
1732 Self::InvalidType(msg.into())
1733 }
1734}
1735
1736impl fmt::Display for InvalidInputError {
1737 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1738 match self {
1739 Self::InvalidType(msg) => write!(f, "{msg}"),
1740 Self::Resource(e) => write!(f, "{e}"),
1741 }
1742 }
1743}
1744
1745impl Error for InvalidInputError {}
1746
1747impl From<ResourceError> for InvalidInputError {
1748 fn from(err: ResourceError) -> Self {
1749 Self::Resource(err)
1750 }
1751}
1752
1753#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1766pub struct MontyFileHandle {
1767 pub path: String,
1769 pub mode: FileMode,
1771 pub position: u64,
1774}
1775
1776impl fmt::Display for MontyFileHandle {
1777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1778 write!(
1779 f,
1780 "<{} name={} mode={}>",
1781 self.mode.file_type_name(),
1782 StringRepr(&self.path),
1783 StringRepr(self.mode.as_str())
1784 )
1785 }
1786}
1787
1788fn monty_datetime_local_micros(datetime: &MontyDateTime) -> Option<i64> {
1789 monty_datetime_naive(datetime).map(|naive| naive.and_utc().timestamp_micros())
1790}
1791
1792fn monty_datetime_raw_eq(a: &MontyDateTime, b: &MontyDateTime) -> bool {
1793 a.year == b.year
1794 && a.month == b.month
1795 && a.day == b.day
1796 && a.hour == b.hour
1797 && a.minute == b.minute
1798 && a.second == b.second
1799 && a.microsecond == b.microsecond
1800 && a.offset_seconds == b.offset_seconds
1801 && a.timezone_name == b.timezone_name
1802}
1803
1804fn monty_datetime_utc_micros(datetime: &MontyDateTime) -> Option<i64> {
1805 let offset_seconds = datetime.offset_seconds?;
1806 let offset_delta = ChronoTimeDelta::try_seconds(i64::from(offset_seconds))?;
1807 let utc = monty_datetime_naive(datetime)?.checked_sub_signed(offset_delta)?;
1808 Some(utc.and_utc().timestamp_micros())
1809}
1810
1811fn monty_datetime_naive(datetime: &MontyDateTime) -> Option<NaiveDateTime> {
1812 let date = NaiveDate::from_ymd_opt(datetime.year, u32::from(datetime.month), u32::from(datetime.day))?;
1813 let time = NaiveTime::from_hms_micro_opt(
1814 u32::from(datetime.hour),
1815 u32::from(datetime.minute),
1816 u32::from(datetime.second),
1817 datetime.microsecond,
1818 )?;
1819 Some(date.and_time(time))
1820}
1821
1822fn decimal_is_zero(text: &str) -> bool {
1827 let unsigned = text.trim().trim_start_matches(['+', '-']);
1828 let mantissa = unsigned.split(['e', 'E']).next().unwrap_or_default();
1829 mantissa.bytes().any(|b| b == b'0') && mantissa.bytes().all(|b| matches!(b, b'0' | b'.' | b'_'))
1830}