1use crate as ast;
19use crate::{Expr, Number};
20use std::borrow::Cow;
21use std::hash::Hash;
22
23#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
24pub enum ComparableBoolOp {
25 And,
26 Or,
27}
28
29impl From<ast::BoolOp> for ComparableBoolOp {
30 fn from(op: ast::BoolOp) -> Self {
31 match op {
32 ast::BoolOp::And => Self::And,
33 ast::BoolOp::Or => Self::Or,
34 }
35 }
36}
37
38#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
39pub enum ComparableOperator {
40 Add,
41 Sub,
42 Mult,
43 MatMult,
44 Div,
45 Mod,
46 Pow,
47 LShift,
48 RShift,
49 BitOr,
50 BitXor,
51 BitAnd,
52 FloorDiv,
53}
54
55impl From<ast::Operator> for ComparableOperator {
56 fn from(op: ast::Operator) -> Self {
57 match op {
58 ast::Operator::Add => Self::Add,
59 ast::Operator::Sub => Self::Sub,
60 ast::Operator::Mult => Self::Mult,
61 ast::Operator::MatMult => Self::MatMult,
62 ast::Operator::Div => Self::Div,
63 ast::Operator::Mod => Self::Mod,
64 ast::Operator::Pow => Self::Pow,
65 ast::Operator::LShift => Self::LShift,
66 ast::Operator::RShift => Self::RShift,
67 ast::Operator::BitOr => Self::BitOr,
68 ast::Operator::BitXor => Self::BitXor,
69 ast::Operator::BitAnd => Self::BitAnd,
70 ast::Operator::FloorDiv => Self::FloorDiv,
71 }
72 }
73}
74
75#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
76pub enum ComparableUnaryOp {
77 Invert,
78 Not,
79 UAdd,
80 USub,
81}
82
83impl From<ast::UnaryOp> for ComparableUnaryOp {
84 fn from(op: ast::UnaryOp) -> Self {
85 match op {
86 ast::UnaryOp::Invert => Self::Invert,
87 ast::UnaryOp::Not => Self::Not,
88 ast::UnaryOp::UAdd => Self::UAdd,
89 ast::UnaryOp::USub => Self::USub,
90 }
91 }
92}
93
94#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
95pub enum ComparableCmpOp {
96 Eq,
97 NotEq,
98 Lt,
99 LtE,
100 Gt,
101 GtE,
102 Is,
103 IsNot,
104 In,
105 NotIn,
106}
107
108impl From<ast::CmpOp> for ComparableCmpOp {
109 fn from(op: ast::CmpOp) -> Self {
110 match op {
111 ast::CmpOp::Eq => Self::Eq,
112 ast::CmpOp::NotEq => Self::NotEq,
113 ast::CmpOp::Lt => Self::Lt,
114 ast::CmpOp::LtE => Self::LtE,
115 ast::CmpOp::Gt => Self::Gt,
116 ast::CmpOp::GtE => Self::GtE,
117 ast::CmpOp::Is => Self::Is,
118 ast::CmpOp::IsNot => Self::IsNot,
119 ast::CmpOp::In => Self::In,
120 ast::CmpOp::NotIn => Self::NotIn,
121 }
122 }
123}
124
125#[derive(Debug, PartialEq, Eq, Hash)]
126pub struct ComparableAlias<'a> {
127 name: &'a str,
128 asname: Option<&'a str>,
129}
130
131impl<'a> From<&'a ast::Alias> for ComparableAlias<'a> {
132 fn from(alias: &'a ast::Alias) -> Self {
133 Self {
134 name: alias.name.as_str(),
135 asname: alias.asname.as_deref(),
136 }
137 }
138}
139
140#[derive(Debug, PartialEq, Eq, Hash)]
141pub struct ComparableWithItem<'a> {
142 context_expr: ComparableExpr<'a>,
143 optional_vars: Option<ComparableExpr<'a>>,
144}
145
146impl<'a> From<&'a ast::WithItem> for ComparableWithItem<'a> {
147 fn from(with_item: &'a ast::WithItem) -> Self {
148 Self {
149 context_expr: (&with_item.context_expr).into(),
150 optional_vars: with_item.optional_vars.as_ref().map(Into::into),
151 }
152 }
153}
154
155#[derive(Debug, PartialEq, Eq, Hash)]
156pub struct ComparablePatternArguments<'a> {
157 patterns: Vec<ComparablePattern<'a>>,
158 keywords: Vec<ComparablePatternKeyword<'a>>,
159}
160
161impl<'a> From<&'a ast::PatternArguments> for ComparablePatternArguments<'a> {
162 fn from(parameters: &'a ast::PatternArguments) -> Self {
163 Self {
164 patterns: parameters.patterns.iter().map(Into::into).collect(),
165 keywords: parameters.keywords.iter().map(Into::into).collect(),
166 }
167 }
168}
169
170#[derive(Debug, PartialEq, Eq, Hash)]
171pub struct ComparablePatternKeyword<'a> {
172 attr: &'a str,
173 pattern: ComparablePattern<'a>,
174}
175
176impl<'a> From<&'a ast::PatternKeyword> for ComparablePatternKeyword<'a> {
177 fn from(keyword: &'a ast::PatternKeyword) -> Self {
178 Self {
179 attr: keyword.attr.as_str(),
180 pattern: (&keyword.pattern).into(),
181 }
182 }
183}
184
185#[derive(Debug, PartialEq, Eq, Hash)]
186pub struct PatternMatchValue<'a> {
187 value: ComparableExpr<'a>,
188}
189
190#[derive(Debug, PartialEq, Eq, Hash)]
191pub struct PatternMatchSingleton {
192 value: ComparableSingleton,
193}
194
195#[derive(Debug, PartialEq, Eq, Hash)]
196pub struct PatternMatchSequence<'a> {
197 patterns: Vec<ComparablePattern<'a>>,
198}
199
200#[derive(Debug, PartialEq, Eq, Hash)]
201pub struct PatternMatchMapping<'a> {
202 keys: Vec<ComparableExpr<'a>>,
203 patterns: Vec<ComparablePattern<'a>>,
204 rest: Option<&'a str>,
205}
206
207#[derive(Debug, PartialEq, Eq, Hash)]
208pub struct PatternMatchClass<'a> {
209 cls: ComparableExpr<'a>,
210 arguments: ComparablePatternArguments<'a>,
211}
212
213#[derive(Debug, PartialEq, Eq, Hash)]
214pub struct PatternMatchStar<'a> {
215 name: Option<&'a str>,
216}
217
218#[derive(Debug, PartialEq, Eq, Hash)]
219pub struct PatternMatchAs<'a> {
220 pattern: Option<Box<ComparablePattern<'a>>>,
221 name: Option<&'a str>,
222}
223
224#[derive(Debug, PartialEq, Eq, Hash)]
225pub struct PatternMatchOr<'a> {
226 patterns: Vec<ComparablePattern<'a>>,
227}
228
229#[derive(Debug, PartialEq, Eq, Hash)]
230pub enum ComparablePattern<'a> {
231 MatchValue(PatternMatchValue<'a>),
232 MatchSingleton(PatternMatchSingleton),
233 MatchSequence(PatternMatchSequence<'a>),
234 MatchMapping(PatternMatchMapping<'a>),
235 MatchClass(PatternMatchClass<'a>),
236 MatchStar(PatternMatchStar<'a>),
237 MatchAs(PatternMatchAs<'a>),
238 MatchOr(PatternMatchOr<'a>),
239}
240
241impl<'a> From<&'a ast::Pattern> for ComparablePattern<'a> {
242 fn from(pattern: &'a ast::Pattern) -> Self {
243 match pattern {
244 ast::Pattern::MatchValue(ast::PatternMatchValue { value, .. }) => {
245 Self::MatchValue(PatternMatchValue {
246 value: value.into(),
247 })
248 }
249 ast::Pattern::MatchSingleton(ast::PatternMatchSingleton { value, .. }) => {
250 Self::MatchSingleton(PatternMatchSingleton {
251 value: value.into(),
252 })
253 }
254 ast::Pattern::MatchSequence(ast::PatternMatchSequence { patterns, .. }) => {
255 Self::MatchSequence(PatternMatchSequence {
256 patterns: patterns.iter().map(Into::into).collect(),
257 })
258 }
259 ast::Pattern::MatchMapping(ast::PatternMatchMapping {
260 keys,
261 patterns,
262 rest,
263 ..
264 }) => Self::MatchMapping(PatternMatchMapping {
265 keys: keys.iter().map(Into::into).collect(),
266 patterns: patterns.iter().map(Into::into).collect(),
267 rest: rest.as_deref(),
268 }),
269 ast::Pattern::MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
270 Self::MatchClass(PatternMatchClass {
271 cls: cls.into(),
272 arguments: arguments.into(),
273 })
274 }
275 ast::Pattern::MatchStar(ast::PatternMatchStar { name, .. }) => {
276 Self::MatchStar(PatternMatchStar {
277 name: name.as_deref(),
278 })
279 }
280 ast::Pattern::MatchAs(ast::PatternMatchAs { pattern, name, .. }) => {
281 Self::MatchAs(PatternMatchAs {
282 pattern: pattern.as_ref().map(Into::into),
283 name: name.as_deref(),
284 })
285 }
286 ast::Pattern::MatchOr(ast::PatternMatchOr { patterns, .. }) => {
287 Self::MatchOr(PatternMatchOr {
288 patterns: patterns.iter().map(Into::into).collect(),
289 })
290 }
291 }
292 }
293}
294
295impl<'a> From<&'a Box<ast::Pattern>> for Box<ComparablePattern<'a>> {
296 fn from(pattern: &'a Box<ast::Pattern>) -> Self {
297 Box::new((pattern.as_ref()).into())
298 }
299}
300
301#[derive(Debug, PartialEq, Eq, Hash)]
302pub struct ComparableMatchCase<'a> {
303 pattern: ComparablePattern<'a>,
304 guard: Option<ComparableExpr<'a>>,
305 body: Vec<ComparableStmt<'a>>,
306}
307
308impl<'a> From<&'a ast::MatchCase> for ComparableMatchCase<'a> {
309 fn from(match_case: &'a ast::MatchCase) -> Self {
310 Self {
311 pattern: (&match_case.pattern).into(),
312 guard: match_case.guard.as_ref().map(Into::into),
313 body: match_case.body.iter().map(Into::into).collect(),
314 }
315 }
316}
317
318#[derive(Debug, PartialEq, Eq, Hash)]
319pub struct ComparableDecorator<'a> {
320 expression: ComparableExpr<'a>,
321}
322
323impl<'a> From<&'a ast::Decorator> for ComparableDecorator<'a> {
324 fn from(decorator: &'a ast::Decorator) -> Self {
325 Self {
326 expression: (&decorator.expression).into(),
327 }
328 }
329}
330
331#[derive(Debug, PartialEq, Eq, Hash)]
332pub enum ComparableSingleton {
333 None,
334 True,
335 False,
336}
337
338impl From<&ast::Singleton> for ComparableSingleton {
339 fn from(singleton: &ast::Singleton) -> Self {
340 match singleton {
341 ast::Singleton::None => Self::None,
342 ast::Singleton::True => Self::True,
343 ast::Singleton::False => Self::False,
344 }
345 }
346}
347
348#[derive(Debug, PartialEq, Eq, Hash)]
349pub enum ComparableNumber<'a> {
350 Int(&'a ast::Int),
351 Float(u64),
352 Complex { real: u64, imag: u64 },
353}
354
355impl<'a> From<&'a ast::Number> for ComparableNumber<'a> {
356 fn from(number: &'a ast::Number) -> Self {
357 match number {
358 ast::Number::Int(value) => Self::Int(value),
359 ast::Number::Float(value) => Self::Float(value.to_bits()),
360 ast::Number::Complex { real, imag } => Self::Complex {
361 real: real.to_bits(),
362 imag: imag.to_bits(),
363 },
364 }
365 }
366}
367
368#[derive(Debug, Default, PartialEq, Eq, Hash)]
369pub struct ComparableArguments<'a> {
370 args: Vec<ComparableExpr<'a>>,
371 keywords: Vec<ComparableKeyword<'a>>,
372}
373
374impl<'a> From<&'a ast::Arguments> for ComparableArguments<'a> {
375 fn from(arguments: &'a ast::Arguments) -> Self {
376 Self {
377 args: arguments.args.iter().map(Into::into).collect(),
378 keywords: arguments.keywords.iter().map(Into::into).collect(),
379 }
380 }
381}
382
383impl<'a> From<&'a Box<ast::Arguments>> for ComparableArguments<'a> {
384 fn from(arguments: &'a Box<ast::Arguments>) -> Self {
385 (arguments.as_ref()).into()
386 }
387}
388
389#[derive(Debug, PartialEq, Eq, Hash)]
390pub struct ComparableParameters<'a> {
391 posonlyargs: Vec<ComparableParameterWithDefault<'a>>,
392 args: Vec<ComparableParameterWithDefault<'a>>,
393 vararg: Option<ComparableParameter<'a>>,
394 kwonlyargs: Vec<ComparableParameterWithDefault<'a>>,
395 kwarg: Option<ComparableParameter<'a>>,
396}
397
398impl<'a> From<&'a ast::Parameters> for ComparableParameters<'a> {
399 fn from(parameters: &'a ast::Parameters) -> Self {
400 Self {
401 posonlyargs: parameters.posonlyargs.iter().map(Into::into).collect(),
402 args: parameters.args.iter().map(Into::into).collect(),
403 vararg: parameters.vararg.as_ref().map(Into::into),
404 kwonlyargs: parameters.kwonlyargs.iter().map(Into::into).collect(),
405 kwarg: parameters.kwarg.as_ref().map(Into::into),
406 }
407 }
408}
409
410impl<'a> From<&'a Box<ast::Parameters>> for ComparableParameters<'a> {
411 fn from(parameters: &'a Box<ast::Parameters>) -> Self {
412 (parameters.as_ref()).into()
413 }
414}
415
416impl<'a> From<&'a Box<ast::Parameter>> for ComparableParameter<'a> {
417 fn from(arg: &'a Box<ast::Parameter>) -> Self {
418 (arg.as_ref()).into()
419 }
420}
421
422#[derive(Debug, PartialEq, Eq, Hash)]
423pub struct ComparableParameter<'a> {
424 arg: &'a str,
425 annotation: Option<Box<ComparableExpr<'a>>>,
426}
427
428impl<'a> From<&'a ast::Parameter> for ComparableParameter<'a> {
429 fn from(arg: &'a ast::Parameter) -> Self {
430 Self {
431 arg: arg.name.as_str(),
432 annotation: arg.annotation.as_ref().map(Into::into),
433 }
434 }
435}
436
437#[derive(Debug, PartialEq, Eq, Hash)]
438pub struct ComparableParameterWithDefault<'a> {
439 def: ComparableParameter<'a>,
440 default: Option<ComparableExpr<'a>>,
441}
442
443impl<'a> From<&'a ast::ParameterWithDefault> for ComparableParameterWithDefault<'a> {
444 fn from(arg: &'a ast::ParameterWithDefault) -> Self {
445 Self {
446 def: (&arg.parameter).into(),
447 default: arg.default.as_ref().map(Into::into),
448 }
449 }
450}
451
452#[derive(Debug, PartialEq, Eq, Hash)]
453pub struct ComparableKeyword<'a> {
454 arg: Option<&'a str>,
455 value: ComparableExpr<'a>,
456}
457
458impl<'a> From<&'a ast::Keyword> for ComparableKeyword<'a> {
459 fn from(keyword: &'a ast::Keyword) -> Self {
460 Self {
461 arg: keyword.arg.as_ref().map(ast::Identifier::as_str),
462 value: (&keyword.value).into(),
463 }
464 }
465}
466
467#[derive(Debug, PartialEq, Eq, Hash)]
468pub struct ComparableComprehension<'a> {
469 target: ComparableExpr<'a>,
470 iter: ComparableExpr<'a>,
471 ifs: Vec<ComparableExpr<'a>>,
472 is_async: bool,
473}
474
475impl<'a> From<&'a ast::Comprehension> for ComparableComprehension<'a> {
476 fn from(comprehension: &'a ast::Comprehension) -> Self {
477 Self {
478 target: (&comprehension.target).into(),
479 iter: (&comprehension.iter).into(),
480 ifs: comprehension.ifs.iter().map(Into::into).collect(),
481 is_async: comprehension.is_async,
482 }
483 }
484}
485
486#[derive(Debug, PartialEq, Eq, Hash)]
487pub struct ExceptHandlerExceptHandler<'a> {
488 type_: Option<Box<ComparableExpr<'a>>>,
489 name: Option<&'a str>,
490 body: Vec<ComparableStmt<'a>>,
491}
492
493#[derive(Debug, PartialEq, Eq, Hash)]
494pub enum ComparableExceptHandler<'a> {
495 ExceptHandler(ExceptHandlerExceptHandler<'a>),
496}
497
498impl<'a> From<&'a ast::ExceptHandler> for ComparableExceptHandler<'a> {
499 fn from(except_handler: &'a ast::ExceptHandler) -> Self {
500 let ast::ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
501 type_,
502 name,
503 body,
504 ..
505 }) = except_handler;
506 Self::ExceptHandler(ExceptHandlerExceptHandler {
507 type_: type_.as_ref().map(Into::into),
508 name: name.as_deref(),
509 body: body.iter().map(Into::into).collect(),
510 })
511 }
512}
513
514#[derive(Debug, PartialEq, Eq, Hash)]
515pub enum ComparableInterpolatedStringElement<'a> {
516 Literal(Cow<'a, str>),
517 InterpolatedElement(InterpolatedElement<'a>),
518}
519
520#[derive(Debug, PartialEq, Eq, Hash)]
521pub struct InterpolatedElement<'a> {
522 expression: ComparableExpr<'a>,
523 debug_text: Option<&'a ast::DebugText>,
524 conversion: ast::ConversionFlag,
525 format_spec: Option<Vec<ComparableInterpolatedStringElement<'a>>>,
526}
527
528impl<'a> From<&'a ast::InterpolatedStringElement> for ComparableInterpolatedStringElement<'a> {
529 fn from(interpolated_string_element: &'a ast::InterpolatedStringElement) -> Self {
530 match interpolated_string_element {
531 ast::InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
532 value,
533 ..
534 }) => Self::Literal(value.as_ref().into()),
535 ast::InterpolatedStringElement::Interpolation(formatted_value) => {
536 formatted_value.into()
537 }
538 }
539 }
540}
541
542impl<'a> From<&'a ast::InterpolatedElement> for InterpolatedElement<'a> {
543 fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
544 let ast::InterpolatedElement {
545 expression,
546 debug_text,
547 conversion,
548 format_spec,
549 range: _,
550 node_index: _,
551 } = interpolated_element;
552
553 Self {
554 expression: (expression).into(),
555 debug_text: debug_text.as_ref(),
556 conversion: *conversion,
557 format_spec: format_spec
558 .as_ref()
559 .map(|spec| spec.elements.iter().map(Into::into).collect()),
560 }
561 }
562}
563
564impl<'a> From<&'a ast::InterpolatedElement> for ComparableInterpolatedStringElement<'a> {
565 fn from(interpolated_element: &'a ast::InterpolatedElement) -> Self {
566 Self::InterpolatedElement(interpolated_element.into())
567 }
568}
569
570#[derive(Debug, PartialEq, Eq, Hash)]
571pub struct ComparableElifElseClause<'a> {
572 test: Option<ComparableExpr<'a>>,
573 body: Vec<ComparableStmt<'a>>,
574}
575
576impl<'a> From<&'a ast::ElifElseClause> for ComparableElifElseClause<'a> {
577 fn from(elif_else_clause: &'a ast::ElifElseClause) -> Self {
578 let ast::ElifElseClause {
579 range: _,
580 node_index: _,
581 test,
582 body,
583 } = elif_else_clause;
584 Self {
585 test: test.as_ref().map(Into::into),
586 body: body.iter().map(Into::into).collect(),
587 }
588 }
589}
590
591#[derive(Debug, PartialEq, Eq, Hash)]
592pub enum ComparableLiteral<'a> {
593 None,
594 Ellipsis,
595 Bool(&'a bool),
596 Str(Vec<ComparableStringLiteral<'a>>),
597 Bytes(Vec<ComparableBytesLiteral<'a>>),
598 Number(ComparableNumber<'a>),
599}
600
601impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
602 fn from(literal: ast::LiteralExpressionRef<'a>) -> Self {
603 match literal {
604 ast::LiteralExpressionRef::NoneLiteral(_) => Self::None,
605 ast::LiteralExpressionRef::EllipsisLiteral(_) => Self::Ellipsis,
606 ast::LiteralExpressionRef::BooleanLiteral(ast::ExprBooleanLiteral {
607 value, ..
608 }) => Self::Bool(value),
609 ast::LiteralExpressionRef::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
610 Self::Str(value.iter().map(Into::into).collect())
611 }
612 ast::LiteralExpressionRef::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
613 Self::Bytes(value.iter().map(Into::into).collect())
614 }
615 ast::LiteralExpressionRef::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
616 Self::Number(value.into())
617 }
618 }
619 }
620}
621
622#[derive(Debug, PartialEq, Eq, Hash)]
623pub struct ComparableFString<'a> {
624 elements: Box<[ComparableInterpolatedStringElement<'a>]>,
625}
626
627impl<'a> From<&'a ast::FStringValue> for ComparableFString<'a> {
628 fn from(value: &'a ast::FStringValue) -> Self {
649 #[derive(Default)]
650 struct Collector<'a> {
651 elements: Vec<ComparableInterpolatedStringElement<'a>>,
652 }
653
654 impl<'a> Collector<'a> {
655 fn push_literal(&mut self, literal: &'a str) {
661 if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
662 self.elements.last_mut()
663 {
664 existing_literal.to_mut().push_str(literal);
665 } else {
666 self.elements
667 .push(ComparableInterpolatedStringElement::Literal(literal.into()));
668 }
669 }
670
671 fn push_expression(&mut self, expression: &'a ast::InterpolatedElement) {
672 self.elements.push(expression.into());
673 }
674 }
675
676 let mut collector = Collector::default();
677
678 for part in value {
679 match part {
680 ast::FStringPart::Literal(string_literal) => {
681 collector.push_literal(&string_literal.value);
682 }
683 ast::FStringPart::FString(fstring) => {
684 for element in &fstring.elements {
685 match element {
686 ast::InterpolatedStringElement::Literal(literal) => {
687 collector.push_literal(&literal.value);
688 }
689 ast::InterpolatedStringElement::Interpolation(expression) => {
690 collector.push_expression(expression);
691 }
692 }
693 }
694 }
695 }
696 }
697
698 Self {
699 elements: collector.elements.into_boxed_slice(),
700 }
701 }
702}
703
704#[derive(Debug, PartialEq, Eq, Hash)]
705pub struct ComparableTString<'a> {
706 strings: Box<[ComparableInterpolatedStringElement<'a>]>,
707 interpolations: Box<[InterpolatedElement<'a>]>,
708}
709
710impl<'a> From<&'a ast::TStringValue> for ComparableTString<'a> {
711 fn from(value: &'a ast::TStringValue) -> Self {
722 struct Collector<'a> {
723 strings: Vec<ComparableInterpolatedStringElement<'a>>,
724 interpolations: Vec<InterpolatedElement<'a>>,
725 }
726
727 impl Default for Collector<'_> {
728 fn default() -> Self {
729 Self {
730 strings: vec![ComparableInterpolatedStringElement::Literal("".into())],
731 interpolations: vec![],
732 }
733 }
734 }
735
736 impl<'a> Collector<'a> {
737 fn push_literal(&mut self, literal: &'a str) {
743 if let Some(ComparableInterpolatedStringElement::Literal(existing_literal)) =
744 self.strings.last_mut()
745 {
746 existing_literal.to_mut().push_str(literal);
747 } else {
748 self.strings
749 .push(ComparableInterpolatedStringElement::Literal(literal.into()));
750 }
751 }
752
753 fn start_new_literal(&mut self) {
754 self.strings
755 .push(ComparableInterpolatedStringElement::Literal("".into()));
756 }
757
758 fn push_tstring_interpolation(&mut self, expression: &'a ast::InterpolatedElement) {
759 self.interpolations.push(expression.into());
760 self.start_new_literal();
761 }
762 }
763
764 let mut collector = Collector::default();
765
766 for element in value.elements() {
767 match element {
768 ast::InterpolatedStringElement::Literal(literal) => {
769 collector.push_literal(&literal.value);
770 }
771 ast::InterpolatedStringElement::Interpolation(interpolation) => {
772 collector.push_tstring_interpolation(interpolation);
773 }
774 }
775 }
776
777 Self {
778 strings: collector.strings.into_boxed_slice(),
779 interpolations: collector.interpolations.into_boxed_slice(),
780 }
781 }
782}
783
784#[derive(Debug, PartialEq, Eq, Hash)]
785pub struct ComparableStringLiteral<'a> {
786 value: &'a str,
787}
788
789impl<'a> From<&'a ast::StringLiteral> for ComparableStringLiteral<'a> {
790 fn from(string_literal: &'a ast::StringLiteral) -> Self {
791 Self {
792 value: &string_literal.value,
793 }
794 }
795}
796
797#[derive(Debug, PartialEq, Eq, Hash)]
798pub struct ComparableBytesLiteral<'a> {
799 value: Cow<'a, [u8]>,
800}
801
802impl<'a> From<&'a ast::BytesLiteral> for ComparableBytesLiteral<'a> {
803 fn from(bytes_literal: &'a ast::BytesLiteral) -> Self {
804 Self {
805 value: Cow::Borrowed(&bytes_literal.value),
806 }
807 }
808}
809
810#[derive(Debug, PartialEq, Eq, Hash)]
811pub struct ExprBoolOp<'a> {
812 op: ComparableBoolOp,
813 values: Vec<ComparableExpr<'a>>,
814}
815
816#[derive(Debug, PartialEq, Eq, Hash)]
817pub struct ExprNamed<'a> {
818 target: Box<ComparableExpr<'a>>,
819 value: Box<ComparableExpr<'a>>,
820}
821
822#[derive(Debug, PartialEq, Eq, Hash)]
823pub struct ExprBinOp<'a> {
824 left: Box<ComparableExpr<'a>>,
825 op: ComparableOperator,
826 right: Box<ComparableExpr<'a>>,
827}
828
829#[derive(Debug, PartialEq, Eq, Hash)]
830pub struct ExprUnaryOp<'a> {
831 op: ComparableUnaryOp,
832 operand: Box<ComparableExpr<'a>>,
833}
834
835#[derive(Debug, PartialEq, Eq, Hash)]
836pub struct ExprLambda<'a> {
837 parameters: Option<ComparableParameters<'a>>,
838 body: Box<ComparableExpr<'a>>,
839}
840
841#[derive(Debug, PartialEq, Eq, Hash)]
842pub struct ExprIf<'a> {
843 test: Box<ComparableExpr<'a>>,
844 body: Box<ComparableExpr<'a>>,
845 orelse: Box<ComparableExpr<'a>>,
846}
847
848#[derive(Debug, PartialEq, Eq, Hash)]
849pub struct ComparableDictItem<'a> {
850 key: Option<ComparableExpr<'a>>,
851 value: ComparableExpr<'a>,
852}
853
854impl<'a> From<&'a ast::DictItem> for ComparableDictItem<'a> {
855 fn from(ast::DictItem { key, value }: &'a ast::DictItem) -> Self {
856 Self {
857 key: key.as_ref().map(ComparableExpr::from),
858 value: value.into(),
859 }
860 }
861}
862
863#[derive(Debug, PartialEq, Eq, Hash)]
864pub struct ExprDict<'a> {
865 items: Vec<ComparableDictItem<'a>>,
866}
867
868#[derive(Debug, PartialEq, Eq, Hash)]
869pub struct ExprSet<'a> {
870 elts: Vec<ComparableExpr<'a>>,
871}
872
873#[derive(Debug, PartialEq, Eq, Hash)]
874pub struct ExprListComp<'a> {
875 elt: Box<ComparableExpr<'a>>,
876 generators: Vec<ComparableComprehension<'a>>,
877}
878
879#[derive(Debug, PartialEq, Eq, Hash)]
880pub struct ExprSetComp<'a> {
881 elt: Box<ComparableExpr<'a>>,
882 generators: Vec<ComparableComprehension<'a>>,
883}
884
885#[derive(Debug, PartialEq, Eq, Hash)]
886pub struct ExprDictComp<'a> {
887 key: Box<ComparableExpr<'a>>,
888 value: Box<ComparableExpr<'a>>,
889 generators: Vec<ComparableComprehension<'a>>,
890}
891
892#[derive(Debug, PartialEq, Eq, Hash)]
893pub struct ExprGenerator<'a> {
894 elt: Box<ComparableExpr<'a>>,
895 generators: Vec<ComparableComprehension<'a>>,
896}
897
898#[derive(Debug, PartialEq, Eq, Hash)]
899pub struct ExprAwait<'a> {
900 value: Box<ComparableExpr<'a>>,
901}
902
903#[derive(Debug, PartialEq, Eq, Hash)]
904pub struct ExprYield<'a> {
905 value: Option<Box<ComparableExpr<'a>>>,
906}
907
908#[derive(Debug, PartialEq, Eq, Hash)]
909pub struct ExprYieldFrom<'a> {
910 value: Box<ComparableExpr<'a>>,
911}
912
913#[derive(Debug, PartialEq, Eq, Hash)]
914pub struct ExprCompare<'a> {
915 left: Box<ComparableExpr<'a>>,
916 ops: Vec<ComparableCmpOp>,
917 comparators: Vec<ComparableExpr<'a>>,
918}
919
920#[derive(Debug, PartialEq, Eq, Hash)]
921pub struct ExprCall<'a> {
922 func: Box<ComparableExpr<'a>>,
923 arguments: ComparableArguments<'a>,
924}
925
926#[derive(Debug, PartialEq, Eq, Hash)]
927pub struct ExprInterpolatedElement<'a> {
928 value: Box<ComparableExpr<'a>>,
929 debug_text: Option<&'a ast::DebugText>,
930 conversion: ast::ConversionFlag,
931 format_spec: Vec<ComparableInterpolatedStringElement<'a>>,
932}
933
934#[derive(Debug, PartialEq, Eq, Hash)]
935pub struct ExprFString<'a> {
936 value: ComparableFString<'a>,
937}
938
939#[derive(Debug, PartialEq, Eq, Hash)]
940pub struct ExprTString<'a> {
941 value: ComparableTString<'a>,
942}
943
944#[derive(Debug, PartialEq, Eq, Hash)]
945pub struct ExprStringLiteral<'a> {
946 value: ComparableStringLiteral<'a>,
947}
948
949#[derive(Debug, PartialEq, Eq, Hash)]
950pub struct ExprBytesLiteral<'a> {
951 value: ComparableBytesLiteral<'a>,
952}
953
954#[derive(Debug, PartialEq, Eq, Hash)]
955pub struct ExprNumberLiteral<'a> {
956 value: ComparableNumber<'a>,
957}
958
959#[derive(Debug, PartialEq, Eq, Hash)]
960pub struct ExprBoolLiteral {
961 value: bool,
962}
963
964#[derive(Debug, PartialEq, Eq, Hash)]
965pub struct ExprAttribute<'a> {
966 value: Box<ComparableExpr<'a>>,
967 attr: &'a str,
968}
969
970#[derive(Debug, PartialEq, Eq, Hash)]
971pub struct ExprSubscript<'a> {
972 value: Box<ComparableExpr<'a>>,
973 slice: Box<ComparableExpr<'a>>,
974}
975
976#[derive(Debug, PartialEq, Eq, Hash)]
977pub struct ExprStarred<'a> {
978 value: Box<ComparableExpr<'a>>,
979}
980
981#[derive(Debug, PartialEq, Eq, Hash)]
982pub struct ExprName<'a> {
983 id: &'a str,
984}
985
986#[derive(Debug, PartialEq, Eq, Hash)]
987pub struct ExprList<'a> {
988 elts: Vec<ComparableExpr<'a>>,
989}
990
991#[derive(Debug, PartialEq, Eq, Hash)]
992pub struct ExprTuple<'a> {
993 elts: Vec<ComparableExpr<'a>>,
994}
995
996#[derive(Debug, PartialEq, Eq, Hash)]
997pub struct ExprSlice<'a> {
998 lower: Option<Box<ComparableExpr<'a>>>,
999 upper: Option<Box<ComparableExpr<'a>>>,
1000 step: Option<Box<ComparableExpr<'a>>>,
1001}
1002
1003#[derive(Debug, PartialEq, Eq, Hash)]
1004pub struct ExprIpyEscapeCommand<'a> {
1005 kind: ast::IpyEscapeKind,
1006 value: &'a str,
1007}
1008
1009#[derive(Debug, PartialEq, Eq, Hash)]
1010pub enum ComparableExpr<'a> {
1011 BoolOp(ExprBoolOp<'a>),
1012 NamedExpr(ExprNamed<'a>),
1013 BinOp(ExprBinOp<'a>),
1014 UnaryOp(ExprUnaryOp<'a>),
1015 Lambda(ExprLambda<'a>),
1016 IfExp(ExprIf<'a>),
1017 Dict(ExprDict<'a>),
1018 Set(ExprSet<'a>),
1019 ListComp(ExprListComp<'a>),
1020 SetComp(ExprSetComp<'a>),
1021 DictComp(ExprDictComp<'a>),
1022 GeneratorExp(ExprGenerator<'a>),
1023 Await(ExprAwait<'a>),
1024 Yield(ExprYield<'a>),
1025 YieldFrom(ExprYieldFrom<'a>),
1026 Compare(ExprCompare<'a>),
1027 Call(ExprCall<'a>),
1028 FStringExpressionElement(ExprInterpolatedElement<'a>),
1029 FString(ExprFString<'a>),
1030 TStringInterpolationElement(ExprInterpolatedElement<'a>),
1031 TString(ExprTString<'a>),
1032 StringLiteral(ExprStringLiteral<'a>),
1033 BytesLiteral(ExprBytesLiteral<'a>),
1034 NumberLiteral(ExprNumberLiteral<'a>),
1035 BoolLiteral(ExprBoolLiteral),
1036 NoneLiteral,
1037 EllipsisLiteral,
1038 Attribute(ExprAttribute<'a>),
1039 Subscript(ExprSubscript<'a>),
1040 Starred(ExprStarred<'a>),
1041 Name(ExprName<'a>),
1042 List(ExprList<'a>),
1043 Tuple(ExprTuple<'a>),
1044 Slice(ExprSlice<'a>),
1045 IpyEscapeCommand(ExprIpyEscapeCommand<'a>),
1046}
1047
1048impl<'a> From<&'a Box<ast::Expr>> for Box<ComparableExpr<'a>> {
1049 fn from(expr: &'a Box<ast::Expr>) -> Self {
1050 Box::new((expr.as_ref()).into())
1051 }
1052}
1053
1054impl<'a> From<&'a Box<ast::Expr>> for ComparableExpr<'a> {
1055 fn from(expr: &'a Box<ast::Expr>) -> Self {
1056 (expr.as_ref()).into()
1057 }
1058}
1059
1060impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
1061 fn from(expr: &'a ast::Expr) -> Self {
1062 match expr {
1063 ast::Expr::BoolOp(ast::ExprBoolOp {
1064 op,
1065 values,
1066 range: _,
1067 node_index: _,
1068 }) => Self::BoolOp(ExprBoolOp {
1069 op: (*op).into(),
1070 values: values.iter().map(Into::into).collect(),
1071 }),
1072 ast::Expr::Named(ast::ExprNamed {
1073 target,
1074 value,
1075 range: _,
1076 node_index: _,
1077 }) => Self::NamedExpr(ExprNamed {
1078 target: target.into(),
1079 value: value.into(),
1080 }),
1081 ast::Expr::BinOp(ast::ExprBinOp {
1082 left,
1083 op,
1084 right,
1085 range: _,
1086 node_index: _,
1087 }) => Self::BinOp(ExprBinOp {
1088 left: left.into(),
1089 op: (*op).into(),
1090 right: right.into(),
1091 }),
1092 ast::Expr::UnaryOp(ast::ExprUnaryOp {
1093 op,
1094 operand,
1095 range: _,
1096 node_index: _,
1097 }) => Self::UnaryOp(ExprUnaryOp {
1098 op: (*op).into(),
1099 operand: operand.into(),
1100 }),
1101 ast::Expr::Lambda(ast::ExprLambda {
1102 parameters,
1103 body,
1104 range: _,
1105 node_index: _,
1106 }) => Self::Lambda(ExprLambda {
1107 parameters: parameters.as_ref().map(Into::into),
1108 body: body.into(),
1109 }),
1110 ast::Expr::If(ast::ExprIf {
1111 test,
1112 body,
1113 orelse,
1114 range: _,
1115 node_index: _,
1116 }) => Self::IfExp(ExprIf {
1117 test: test.into(),
1118 body: body.into(),
1119 orelse: orelse.into(),
1120 }),
1121 ast::Expr::Dict(ast::ExprDict {
1122 items,
1123 range: _,
1124 node_index: _,
1125 }) => Self::Dict(ExprDict {
1126 items: items.iter().map(ComparableDictItem::from).collect(),
1127 }),
1128 ast::Expr::Set(ast::ExprSet {
1129 elts,
1130 range: _,
1131 node_index: _,
1132 }) => Self::Set(ExprSet {
1133 elts: elts.iter().map(Into::into).collect(),
1134 }),
1135 ast::Expr::ListComp(ast::ExprListComp {
1136 elt,
1137 generators,
1138 range: _,
1139 node_index: _,
1140 }) => Self::ListComp(ExprListComp {
1141 elt: elt.into(),
1142 generators: generators.iter().map(Into::into).collect(),
1143 }),
1144 ast::Expr::SetComp(ast::ExprSetComp {
1145 elt,
1146 generators,
1147 range: _,
1148 node_index: _,
1149 }) => Self::SetComp(ExprSetComp {
1150 elt: elt.into(),
1151 generators: generators.iter().map(Into::into).collect(),
1152 }),
1153 ast::Expr::DictComp(ast::ExprDictComp {
1154 key,
1155 value,
1156 generators,
1157 range: _,
1158 node_index: _,
1159 }) => Self::DictComp(ExprDictComp {
1160 key: key.into(),
1161 value: value.into(),
1162 generators: generators.iter().map(Into::into).collect(),
1163 }),
1164 ast::Expr::Generator(ast::ExprGenerator {
1165 elt,
1166 generators,
1167 range: _,
1168 node_index: _,
1169 parenthesized: _,
1170 }) => Self::GeneratorExp(ExprGenerator {
1171 elt: elt.into(),
1172 generators: generators.iter().map(Into::into).collect(),
1173 }),
1174 ast::Expr::Await(ast::ExprAwait {
1175 value,
1176 range: _,
1177 node_index: _,
1178 }) => Self::Await(ExprAwait {
1179 value: value.into(),
1180 }),
1181 ast::Expr::Yield(ast::ExprYield {
1182 value,
1183 range: _,
1184 node_index: _,
1185 }) => Self::Yield(ExprYield {
1186 value: value.as_ref().map(Into::into),
1187 }),
1188 ast::Expr::YieldFrom(ast::ExprYieldFrom {
1189 value,
1190 range: _,
1191 node_index: _,
1192 }) => Self::YieldFrom(ExprYieldFrom {
1193 value: value.into(),
1194 }),
1195 ast::Expr::Compare(ast::ExprCompare {
1196 left,
1197 ops,
1198 comparators,
1199 range: _,
1200 node_index: _,
1201 }) => Self::Compare(ExprCompare {
1202 left: left.into(),
1203 ops: ops.iter().copied().map(Into::into).collect(),
1204 comparators: comparators.iter().map(Into::into).collect(),
1205 }),
1206 ast::Expr::Call(ast::ExprCall {
1207 func,
1208 arguments,
1209 range: _,
1210 node_index: _,
1211 }) => Self::Call(ExprCall {
1212 func: func.into(),
1213 arguments: arguments.into(),
1214 }),
1215 ast::Expr::FString(ast::ExprFString {
1216 value,
1217 range: _,
1218 node_index: _,
1219 }) => Self::FString(ExprFString {
1220 value: value.into(),
1221 }),
1222 ast::Expr::TString(ast::ExprTString {
1223 value,
1224 range: _,
1225 node_index: _,
1226 }) => Self::TString(ExprTString {
1227 value: value.into(),
1228 }),
1229 ast::Expr::StringLiteral(ast::ExprStringLiteral {
1230 value,
1231 range: _,
1232 node_index: _,
1233 }) => Self::StringLiteral(ExprStringLiteral {
1234 value: ComparableStringLiteral {
1235 value: value.to_str(),
1236 },
1237 }),
1238 ast::Expr::BytesLiteral(ast::ExprBytesLiteral {
1239 value,
1240 range: _,
1241 node_index: _,
1242 }) => Self::BytesLiteral(ExprBytesLiteral {
1243 value: ComparableBytesLiteral {
1244 value: Cow::from(value),
1245 },
1246 }),
1247 ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
1248 value,
1249 range: _,
1250 node_index: _,
1251 }) => Self::NumberLiteral(ExprNumberLiteral {
1252 value: value.into(),
1253 }),
1254 ast::Expr::BooleanLiteral(ast::ExprBooleanLiteral {
1255 value,
1256 range: _,
1257 node_index: _,
1258 }) => Self::BoolLiteral(ExprBoolLiteral { value: *value }),
1259 ast::Expr::NoneLiteral(_) => Self::NoneLiteral,
1260 ast::Expr::EllipsisLiteral(_) => Self::EllipsisLiteral,
1261 ast::Expr::Attribute(ast::ExprAttribute {
1262 value,
1263 attr,
1264 ctx: _,
1265 range: _,
1266 node_index: _,
1267 }) => Self::Attribute(ExprAttribute {
1268 value: value.into(),
1269 attr: attr.as_str(),
1270 }),
1271 ast::Expr::Subscript(ast::ExprSubscript {
1272 value,
1273 slice,
1274 ctx: _,
1275 range: _,
1276 node_index: _,
1277 }) => Self::Subscript(ExprSubscript {
1278 value: value.into(),
1279 slice: slice.into(),
1280 }),
1281 ast::Expr::Starred(ast::ExprStarred {
1282 value,
1283 ctx: _,
1284 range: _,
1285 node_index: _,
1286 }) => Self::Starred(ExprStarred {
1287 value: value.into(),
1288 }),
1289 ast::Expr::Name(name) => name.into(),
1290 ast::Expr::List(ast::ExprList {
1291 elts,
1292 ctx: _,
1293 range: _,
1294 node_index: _,
1295 }) => Self::List(ExprList {
1296 elts: elts.iter().map(Into::into).collect(),
1297 }),
1298 ast::Expr::Tuple(ast::ExprTuple {
1299 elts,
1300 ctx: _,
1301 range: _,
1302 node_index: _,
1303 parenthesized: _,
1304 }) => Self::Tuple(ExprTuple {
1305 elts: elts.iter().map(Into::into).collect(),
1306 }),
1307 ast::Expr::Slice(ast::ExprSlice {
1308 lower,
1309 upper,
1310 step,
1311 range: _,
1312 node_index: _,
1313 }) => Self::Slice(ExprSlice {
1314 lower: lower.as_ref().map(Into::into),
1315 upper: upper.as_ref().map(Into::into),
1316 step: step.as_ref().map(Into::into),
1317 }),
1318 ast::Expr::IpyEscapeCommand(ast::ExprIpyEscapeCommand {
1319 kind,
1320 value,
1321 range: _,
1322 node_index: _,
1323 }) => Self::IpyEscapeCommand(ExprIpyEscapeCommand { kind: *kind, value }),
1324 }
1325 }
1326}
1327
1328impl<'a> From<&'a ast::ExprName> for ComparableExpr<'a> {
1329 fn from(expr: &'a ast::ExprName) -> Self {
1330 Self::Name(ExprName {
1331 id: expr.id.as_str(),
1332 })
1333 }
1334}
1335
1336#[derive(Debug, PartialEq, Eq, Hash)]
1337pub struct StmtFunctionDef<'a> {
1338 is_async: bool,
1339 decorator_list: Vec<ComparableDecorator<'a>>,
1340 name: &'a str,
1341 type_params: Option<ComparableTypeParams<'a>>,
1342 parameters: ComparableParameters<'a>,
1343 returns: Option<ComparableExpr<'a>>,
1344 body: Vec<ComparableStmt<'a>>,
1345}
1346
1347#[derive(Debug, PartialEq, Eq, Hash)]
1348pub struct StmtClassDef<'a> {
1349 decorator_list: Vec<ComparableDecorator<'a>>,
1350 name: &'a str,
1351 type_params: Option<ComparableTypeParams<'a>>,
1352 arguments: ComparableArguments<'a>,
1353 body: Vec<ComparableStmt<'a>>,
1354}
1355
1356#[derive(Debug, PartialEq, Eq, Hash)]
1357pub struct StmtReturn<'a> {
1358 value: Option<ComparableExpr<'a>>,
1359}
1360
1361#[derive(Debug, PartialEq, Eq, Hash)]
1362pub struct StmtDelete<'a> {
1363 targets: Vec<ComparableExpr<'a>>,
1364}
1365
1366#[derive(Debug, PartialEq, Eq, Hash)]
1367pub struct StmtTypeAlias<'a> {
1368 pub name: Box<ComparableExpr<'a>>,
1369 pub type_params: Option<ComparableTypeParams<'a>>,
1370 pub value: Box<ComparableExpr<'a>>,
1371}
1372
1373#[derive(Debug, PartialEq, Eq, Hash)]
1374pub struct ComparableTypeParams<'a> {
1375 pub type_params: Vec<ComparableTypeParam<'a>>,
1376}
1377
1378impl<'a> From<&'a ast::TypeParams> for ComparableTypeParams<'a> {
1379 fn from(type_params: &'a ast::TypeParams) -> Self {
1380 Self {
1381 type_params: type_params.iter().map(Into::into).collect(),
1382 }
1383 }
1384}
1385
1386impl<'a> From<&'a Box<ast::TypeParams>> for ComparableTypeParams<'a> {
1387 fn from(type_params: &'a Box<ast::TypeParams>) -> Self {
1388 type_params.as_ref().into()
1389 }
1390}
1391
1392#[derive(Debug, PartialEq, Eq, Hash)]
1393pub enum ComparableTypeParam<'a> {
1394 TypeVar(TypeParamTypeVar<'a>),
1395 ParamSpec(TypeParamParamSpec<'a>),
1396 TypeVarTuple(TypeParamTypeVarTuple<'a>),
1397}
1398
1399impl<'a> From<&'a ast::TypeParam> for ComparableTypeParam<'a> {
1400 fn from(type_param: &'a ast::TypeParam) -> Self {
1401 match type_param {
1402 ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
1403 name,
1404 bound,
1405 default,
1406 range: _,
1407 node_index: _,
1408 }) => Self::TypeVar(TypeParamTypeVar {
1409 name: name.as_str(),
1410 bound: bound.as_ref().map(Into::into),
1411 default: default.as_ref().map(Into::into),
1412 }),
1413 ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple {
1414 name,
1415 default,
1416 range: _,
1417 node_index: _,
1418 }) => Self::TypeVarTuple(TypeParamTypeVarTuple {
1419 name: name.as_str(),
1420 default: default.as_ref().map(Into::into),
1421 }),
1422 ast::TypeParam::ParamSpec(ast::TypeParamParamSpec {
1423 name,
1424 default,
1425 range: _,
1426 node_index: _,
1427 }) => Self::ParamSpec(TypeParamParamSpec {
1428 name: name.as_str(),
1429 default: default.as_ref().map(Into::into),
1430 }),
1431 }
1432 }
1433}
1434
1435#[derive(Debug, PartialEq, Eq, Hash)]
1436pub struct TypeParamTypeVar<'a> {
1437 pub name: &'a str,
1438 pub bound: Option<Box<ComparableExpr<'a>>>,
1439 pub default: Option<Box<ComparableExpr<'a>>>,
1440}
1441
1442#[derive(Debug, PartialEq, Eq, Hash)]
1443pub struct TypeParamParamSpec<'a> {
1444 pub name: &'a str,
1445 pub default: Option<Box<ComparableExpr<'a>>>,
1446}
1447
1448#[derive(Debug, PartialEq, Eq, Hash)]
1449pub struct TypeParamTypeVarTuple<'a> {
1450 pub name: &'a str,
1451 pub default: Option<Box<ComparableExpr<'a>>>,
1452}
1453
1454#[derive(Debug, PartialEq, Eq, Hash)]
1455pub struct StmtAssign<'a> {
1456 targets: Vec<ComparableExpr<'a>>,
1457 value: ComparableExpr<'a>,
1458}
1459
1460#[derive(Debug, PartialEq, Eq, Hash)]
1461pub struct StmtAugAssign<'a> {
1462 target: ComparableExpr<'a>,
1463 op: ComparableOperator,
1464 value: ComparableExpr<'a>,
1465}
1466
1467#[derive(Debug, PartialEq, Eq, Hash)]
1468pub struct StmtAnnAssign<'a> {
1469 target: ComparableExpr<'a>,
1470 annotation: ComparableExpr<'a>,
1471 value: Option<ComparableExpr<'a>>,
1472 simple: bool,
1473}
1474
1475#[derive(Debug, PartialEq, Eq, Hash)]
1476pub struct StmtFor<'a> {
1477 is_async: bool,
1478 target: ComparableExpr<'a>,
1479 iter: ComparableExpr<'a>,
1480 body: Vec<ComparableStmt<'a>>,
1481 orelse: Vec<ComparableStmt<'a>>,
1482}
1483
1484#[derive(Debug, PartialEq, Eq, Hash)]
1485pub struct StmtWhile<'a> {
1486 test: ComparableExpr<'a>,
1487 body: Vec<ComparableStmt<'a>>,
1488 orelse: Vec<ComparableStmt<'a>>,
1489}
1490
1491#[derive(Debug, PartialEq, Eq, Hash)]
1492pub struct StmtIf<'a> {
1493 test: ComparableExpr<'a>,
1494 body: Vec<ComparableStmt<'a>>,
1495 elif_else_clauses: Vec<ComparableElifElseClause<'a>>,
1496}
1497
1498#[derive(Debug, PartialEq, Eq, Hash)]
1499pub struct StmtWith<'a> {
1500 is_async: bool,
1501 items: Vec<ComparableWithItem<'a>>,
1502 body: Vec<ComparableStmt<'a>>,
1503}
1504
1505#[derive(Debug, PartialEq, Eq, Hash)]
1506pub struct StmtMatch<'a> {
1507 subject: ComparableExpr<'a>,
1508 cases: Vec<ComparableMatchCase<'a>>,
1509}
1510
1511#[derive(Debug, PartialEq, Eq, Hash)]
1512pub struct StmtRaise<'a> {
1513 exc: Option<ComparableExpr<'a>>,
1514 cause: Option<ComparableExpr<'a>>,
1515}
1516
1517#[derive(Debug, PartialEq, Eq, Hash)]
1518pub struct StmtTry<'a> {
1519 body: Vec<ComparableStmt<'a>>,
1520 handlers: Vec<ComparableExceptHandler<'a>>,
1521 orelse: Vec<ComparableStmt<'a>>,
1522 finalbody: Vec<ComparableStmt<'a>>,
1523 is_star: bool,
1524}
1525
1526#[derive(Debug, PartialEq, Eq, Hash)]
1527pub struct StmtAssert<'a> {
1528 test: ComparableExpr<'a>,
1529 msg: Option<ComparableExpr<'a>>,
1530}
1531
1532#[derive(Debug, PartialEq, Eq, Hash)]
1533pub struct StmtImport<'a> {
1534 names: Vec<ComparableAlias<'a>>,
1535 is_lazy: bool,
1536}
1537
1538#[derive(Debug, PartialEq, Eq, Hash)]
1539pub struct StmtImportFrom<'a> {
1540 module: Option<&'a str>,
1541 names: Vec<ComparableAlias<'a>>,
1542 level: u32,
1543 is_lazy: bool,
1544}
1545
1546#[derive(Debug, PartialEq, Eq, Hash)]
1547pub struct StmtGlobal<'a> {
1548 names: Vec<&'a str>,
1549}
1550
1551#[derive(Debug, PartialEq, Eq, Hash)]
1552pub struct StmtNonlocal<'a> {
1553 names: Vec<&'a str>,
1554}
1555
1556#[derive(Debug, PartialEq, Eq, Hash)]
1557pub struct StmtExpr<'a> {
1558 value: ComparableExpr<'a>,
1559}
1560
1561#[derive(Debug, PartialEq, Eq, Hash)]
1562pub struct StmtIpyEscapeCommand<'a> {
1563 kind: ast::IpyEscapeKind,
1564 value: &'a str,
1565}
1566
1567#[derive(Debug, PartialEq, Eq, Hash)]
1568pub enum ComparableStmt<'a> {
1569 FunctionDef(StmtFunctionDef<'a>),
1570 ClassDef(StmtClassDef<'a>),
1571 Return(StmtReturn<'a>),
1572 Delete(StmtDelete<'a>),
1573 Assign(StmtAssign<'a>),
1574 AugAssign(StmtAugAssign<'a>),
1575 AnnAssign(StmtAnnAssign<'a>),
1576 For(StmtFor<'a>),
1577 While(StmtWhile<'a>),
1578 If(StmtIf<'a>),
1579 With(StmtWith<'a>),
1580 Match(StmtMatch<'a>),
1581 Raise(StmtRaise<'a>),
1582 Try(StmtTry<'a>),
1583 TypeAlias(StmtTypeAlias<'a>),
1584 Assert(StmtAssert<'a>),
1585 Import(StmtImport<'a>),
1586 ImportFrom(StmtImportFrom<'a>),
1587 Global(StmtGlobal<'a>),
1588 Nonlocal(StmtNonlocal<'a>),
1589 IpyEscapeCommand(StmtIpyEscapeCommand<'a>),
1590 Expr(StmtExpr<'a>),
1591 Pass,
1592 Break,
1593 Continue,
1594}
1595
1596impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
1597 fn from(stmt: &'a ast::Stmt) -> Self {
1598 match stmt {
1599 ast::Stmt::FunctionDef(ast::StmtFunctionDef {
1600 is_async,
1601 name,
1602 parameters,
1603 body,
1604 decorator_list,
1605 returns,
1606 type_params,
1607 range: _,
1608 node_index: _,
1609 }) => Self::FunctionDef(StmtFunctionDef {
1610 is_async: *is_async,
1611 name: name.as_str(),
1612 parameters: parameters.into(),
1613 body: body.iter().map(Into::into).collect(),
1614 decorator_list: decorator_list.iter().map(Into::into).collect(),
1615 returns: returns.as_ref().map(Into::into),
1616 type_params: type_params.as_ref().map(Into::into),
1617 }),
1618 ast::Stmt::ClassDef(ast::StmtClassDef {
1619 name,
1620 arguments,
1621 body,
1622 decorator_list,
1623 type_params,
1624 range: _,
1625 node_index: _,
1626 }) => Self::ClassDef(StmtClassDef {
1627 name: name.as_str(),
1628 arguments: arguments.as_ref().map(Into::into).unwrap_or_default(),
1629 body: body.iter().map(Into::into).collect(),
1630 decorator_list: decorator_list.iter().map(Into::into).collect(),
1631 type_params: type_params.as_ref().map(Into::into),
1632 }),
1633 ast::Stmt::Return(ast::StmtReturn {
1634 value,
1635 range: _,
1636 node_index: _,
1637 }) => Self::Return(StmtReturn {
1638 value: value.as_ref().map(Into::into),
1639 }),
1640 ast::Stmt::Delete(ast::StmtDelete {
1641 targets,
1642 range: _,
1643 node_index: _,
1644 }) => Self::Delete(StmtDelete {
1645 targets: targets.iter().map(Into::into).collect(),
1646 }),
1647 ast::Stmt::TypeAlias(ast::StmtTypeAlias {
1648 range: _,
1649 node_index: _,
1650 name,
1651 type_params,
1652 value,
1653 }) => Self::TypeAlias(StmtTypeAlias {
1654 name: name.into(),
1655 type_params: type_params.as_ref().map(Into::into),
1656 value: value.into(),
1657 }),
1658 ast::Stmt::Assign(ast::StmtAssign {
1659 targets,
1660 value,
1661 range: _,
1662 node_index: _,
1663 }) => Self::Assign(StmtAssign {
1664 targets: targets.iter().map(Into::into).collect(),
1665 value: value.into(),
1666 }),
1667 ast::Stmt::AugAssign(ast::StmtAugAssign {
1668 target,
1669 op,
1670 value,
1671 range: _,
1672 node_index: _,
1673 }) => Self::AugAssign(StmtAugAssign {
1674 target: target.into(),
1675 op: (*op).into(),
1676 value: value.into(),
1677 }),
1678 ast::Stmt::AnnAssign(ast::StmtAnnAssign {
1679 target,
1680 annotation,
1681 value,
1682 simple,
1683 range: _,
1684 node_index: _,
1685 }) => Self::AnnAssign(StmtAnnAssign {
1686 target: target.into(),
1687 annotation: annotation.into(),
1688 value: value.as_ref().map(Into::into),
1689 simple: *simple,
1690 }),
1691 ast::Stmt::For(ast::StmtFor {
1692 is_async,
1693 target,
1694 iter,
1695 body,
1696 orelse,
1697 range: _,
1698 node_index: _,
1699 }) => Self::For(StmtFor {
1700 is_async: *is_async,
1701 target: target.into(),
1702 iter: iter.into(),
1703 body: body.iter().map(Into::into).collect(),
1704 orelse: orelse.iter().map(Into::into).collect(),
1705 }),
1706 ast::Stmt::While(ast::StmtWhile {
1707 test,
1708 body,
1709 orelse,
1710 range: _,
1711 node_index: _,
1712 }) => Self::While(StmtWhile {
1713 test: test.into(),
1714 body: body.iter().map(Into::into).collect(),
1715 orelse: orelse.iter().map(Into::into).collect(),
1716 }),
1717 ast::Stmt::If(ast::StmtIf {
1718 test,
1719 body,
1720 elif_else_clauses,
1721 range: _,
1722 node_index: _,
1723 }) => Self::If(StmtIf {
1724 test: test.into(),
1725 body: body.iter().map(Into::into).collect(),
1726 elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(),
1727 }),
1728 ast::Stmt::With(ast::StmtWith {
1729 is_async,
1730 items,
1731 body,
1732 range: _,
1733 node_index: _,
1734 }) => Self::With(StmtWith {
1735 is_async: *is_async,
1736 items: items.iter().map(Into::into).collect(),
1737 body: body.iter().map(Into::into).collect(),
1738 }),
1739 ast::Stmt::Match(ast::StmtMatch {
1740 subject,
1741 cases,
1742 range: _,
1743 node_index: _,
1744 }) => Self::Match(StmtMatch {
1745 subject: subject.into(),
1746 cases: cases.iter().map(Into::into).collect(),
1747 }),
1748 ast::Stmt::Raise(ast::StmtRaise {
1749 exc,
1750 cause,
1751 range: _,
1752 node_index: _,
1753 }) => Self::Raise(StmtRaise {
1754 exc: exc.as_ref().map(Into::into),
1755 cause: cause.as_ref().map(Into::into),
1756 }),
1757 ast::Stmt::Try(ast::StmtTry {
1758 body,
1759 handlers,
1760 orelse,
1761 finalbody,
1762 is_star,
1763 range: _,
1764 node_index: _,
1765 }) => Self::Try(StmtTry {
1766 body: body.iter().map(Into::into).collect(),
1767 handlers: handlers.iter().map(Into::into).collect(),
1768 orelse: orelse.iter().map(Into::into).collect(),
1769 finalbody: finalbody.iter().map(Into::into).collect(),
1770 is_star: *is_star,
1771 }),
1772 ast::Stmt::Assert(ast::StmtAssert {
1773 test,
1774 msg,
1775 range: _,
1776 node_index: _,
1777 }) => Self::Assert(StmtAssert {
1778 test: test.into(),
1779 msg: msg.as_ref().map(Into::into),
1780 }),
1781 ast::Stmt::Import(ast::StmtImport {
1782 names,
1783 is_lazy,
1784 range: _,
1785 node_index: _,
1786 }) => Self::Import(StmtImport {
1787 names: names.iter().map(Into::into).collect(),
1788 is_lazy: *is_lazy,
1789 }),
1790 ast::Stmt::ImportFrom(ast::StmtImportFrom {
1791 module,
1792 names,
1793 level,
1794 is_lazy,
1795 range: _,
1796 node_index: _,
1797 }) => Self::ImportFrom(StmtImportFrom {
1798 module: module.as_deref(),
1799 names: names.iter().map(Into::into).collect(),
1800 level: *level,
1801 is_lazy: *is_lazy,
1802 }),
1803 ast::Stmt::Global(ast::StmtGlobal {
1804 names,
1805 range: _,
1806 node_index: _,
1807 }) => Self::Global(StmtGlobal {
1808 names: names.iter().map(ast::Identifier::as_str).collect(),
1809 }),
1810 ast::Stmt::Nonlocal(ast::StmtNonlocal {
1811 names,
1812 range: _,
1813 node_index: _,
1814 }) => Self::Nonlocal(StmtNonlocal {
1815 names: names.iter().map(ast::Identifier::as_str).collect(),
1816 }),
1817 ast::Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand {
1818 kind,
1819 value,
1820 range: _,
1821 node_index: _,
1822 }) => Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value }),
1823 ast::Stmt::Expr(ast::StmtExpr {
1824 value,
1825 range: _,
1826 node_index: _,
1827 }) => Self::Expr(StmtExpr {
1828 value: value.into(),
1829 }),
1830 ast::Stmt::Pass(_) => Self::Pass,
1831 ast::Stmt::Break(_) => Self::Break,
1832 ast::Stmt::Continue(_) => Self::Continue,
1833 }
1834 }
1835}
1836
1837#[derive(Debug, PartialEq, Eq, Hash)]
1838pub enum ComparableMod<'a> {
1839 Module(ComparableModModule<'a>),
1840 Expression(ComparableModExpression<'a>),
1841}
1842
1843#[derive(Debug, PartialEq, Eq, Hash)]
1844pub struct ComparableModModule<'a> {
1845 body: Vec<ComparableStmt<'a>>,
1846}
1847
1848#[derive(Debug, PartialEq, Eq, Hash)]
1849pub struct ComparableModExpression<'a> {
1850 body: Box<ComparableExpr<'a>>,
1851}
1852
1853impl<'a> From<&'a ast::Mod> for ComparableMod<'a> {
1854 fn from(mod_: &'a ast::Mod) -> Self {
1855 match mod_ {
1856 ast::Mod::Module(module) => Self::Module(module.into()),
1857 ast::Mod::Expression(expr) => Self::Expression(expr.into()),
1858 }
1859 }
1860}
1861
1862impl<'a> From<&'a ast::ModModule> for ComparableModModule<'a> {
1863 fn from(module: &'a ast::ModModule) -> Self {
1864 Self {
1865 body: module.body.iter().map(Into::into).collect(),
1866 }
1867 }
1868}
1869
1870impl<'a> From<&'a ast::ModExpression> for ComparableModExpression<'a> {
1871 fn from(expr: &'a ast::ModExpression) -> Self {
1872 Self {
1873 body: (&expr.body).into(),
1874 }
1875 }
1876}
1877
1878#[derive(Debug)]
1889pub struct HashableExpr<'a>(ComparableExpr<'a>);
1890
1891impl Hash for HashableExpr<'_> {
1892 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1893 self.0.hash(state);
1894 }
1895}
1896
1897impl PartialEq<Self> for HashableExpr<'_> {
1898 fn eq(&self, other: &Self) -> bool {
1899 self.0 == other.0
1900 }
1901}
1902
1903impl Eq for HashableExpr<'_> {}
1904
1905impl<'a> From<&'a Expr> for HashableExpr<'a> {
1906 fn from(expr: &'a Expr) -> Self {
1907 fn as_hashable(expr: &Expr) -> ComparableExpr<'_> {
1910 match expr {
1911 Expr::Named(named) => ComparableExpr::NamedExpr(ExprNamed {
1912 target: Box::new(ComparableExpr::from(&named.target)),
1913 value: Box::new(as_hashable(&named.value)),
1914 }),
1915 Expr::NumberLiteral(number) => as_bool(number)
1916 .map(|value| ComparableExpr::BoolLiteral(ExprBoolLiteral { value }))
1917 .unwrap_or_else(|| ComparableExpr::from(expr)),
1918 Expr::Tuple(tuple) => ComparableExpr::Tuple(ExprTuple {
1919 elts: tuple.iter().map(as_hashable).collect(),
1920 }),
1921 _ => ComparableExpr::from(expr),
1922 }
1923 }
1924
1925 fn as_bool(number: &crate::ExprNumberLiteral) -> Option<bool> {
1928 match &number.value {
1929 Number::Int(int) => match int.as_u8() {
1930 Some(0) => Some(false),
1931 Some(1) => Some(true),
1932 _ => None,
1933 },
1934 Number::Float(float) => match float {
1935 0.0 => Some(false),
1936 1.0 => Some(true),
1937 _ => None,
1938 },
1939 Number::Complex { real, imag } => match (real, imag) {
1940 (0.0, 0.0) => Some(false),
1941 (1.0, 0.0) => Some(true),
1942 _ => None,
1943 },
1944 }
1945 }
1946
1947 Self(as_hashable(expr))
1948 }
1949}