| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // @dart = 2.8 |
| |
| /// This library defines the query report export format. |
| /// |
| /// ## Overview |
| /// |
| /// This abstract syntax tree (AST) is designed for printing query reports. |
| /// Its generic hierarchical nature could render into a variety of formats: |
| /// terminal, JSON, HTML, etc. |
| library ast; |
| |
| import 'dart:core'; |
| |
| import 'package:collection/collection.dart'; |
| |
| import '../codesize.dart'; |
| |
| /// Anything that could be used as the content of a row. |
| /// A title could be plain text (see `StyledString.plain`), some styled text |
| /// (see `StyledString`), or some structured object (see `SizeRecord`). |
| abstract class Title {} |
| |
| /// Helper type to erase the generic argument from `Node<T>`. |
| abstract class AnyNode { |
| Title get title; |
| List<AnyNode> get children; |
| |
| @override |
| bool operator ==(Object other) => |
| other is AnyNode && |
| other.title == title && |
| const ListEquality().equals(other.children, children); |
| |
| @override |
| int get hashCode => title.hashCode ^ const ListEquality().hash(children); |
| } |
| |
| /// The building block of the hierarchial AST. |
| /// Each node has a title, and optionally some children. |
| class Node<T extends Title> extends AnyNode { |
| @override |
| final T title; |
| |
| @override |
| final List<AnyNode> children; |
| |
| Node({this.title, this.children = const []}); |
| |
| /// Creates a simple node with plain text as title, and no children. |
| // Use static methods due to https://github.com/dart-lang/sdk/issues/26391 |
| // ignore: prefer_constructors_over_static_methods |
| static Node<StyledString> plain(String plain) => |
| Node(title: StyledString.plain(plain), children: []); |
| |
| @override |
| String toString() => '''$runtimeType( |
| title: $title, |
| children: $children |
| )'''; |
| } |
| |
| // Styled string --------------------------------------------------------------- |
| |
| abstract class StringPiece {} |
| |
| class StyledString extends Title implements StringPiece { |
| final List<StringPiece> details; |
| |
| StyledString(this.details); |
| |
| StyledString.plain(String plain) : details = ([Plain(plain)]); |
| |
| @override |
| bool operator ==(Object other) => |
| other is StyledString && |
| const ListEquality().equals(other.details, details); |
| |
| @override |
| int get hashCode => const ListEquality().hash(details); |
| } |
| |
| /// Plain text. |
| class Plain implements StringPiece { |
| final String text; |
| |
| Plain(this.text); |
| |
| @override |
| bool operator ==(Object other) => other is Plain && other.text == text; |
| |
| @override |
| int get hashCode => text.hashCode; |
| |
| @override |
| String toString() => 'Plain(text: $text)'; |
| } |
| |
| enum Color { white, green, gray } |
| |
| /// Instructs the renderer to color the contained `StringPiece` a certain color. |
| class AddColor extends StyledString { |
| final Color color; |
| |
| AddColor(this.color, StringPiece piece) : super([piece]); |
| |
| AddColor.white(StringPiece piece) |
| : color = Color.white, |
| super([piece]); |
| AddColor.green(StringPiece piece) |
| : color = Color.green, |
| super([piece]); |
| AddColor.gray(StringPiece piece) |
| : color = Color.gray, |
| super([piece]); |
| |
| @override |
| bool operator ==(Object other) => |
| other is AddColor && |
| const ListEquality().equals(other.details, details) && |
| other.color == color; |
| |
| @override |
| int get hashCode => const ListEquality().hash(details) ^ color.hashCode; |
| |
| @override |
| String toString() => 'AddColor(color: $color, piece: ${details.first})'; |
| } |
| |
| // Custom title components ----------------------------------------------------- |
| |
| class SizeRecord extends Title { |
| final StyledString name; |
| final Tally tally; |
| |
| SizeRecord({this.name, this.tally}); |
| |
| @override |
| bool operator ==(Object other) => |
| other is SizeRecord && other.name == name && other.tally == tally; |
| |
| @override |
| int get hashCode => name.hashCode ^ tally.hashCode; |
| |
| @override |
| String toString() => 'SizeRecord(name: \'$name\', tally: $tally)'; |
| } |
| |
| class UniqueSymbolSizeRecord extends SizeRecord { |
| final List<StyledString> categories; |
| final List<StyledString> rustCrates; |
| |
| UniqueSymbolSizeRecord( |
| {StyledString name, Tally tally, this.categories, this.rustCrates}) |
| : super(name: name, tally: tally); |
| |
| @override |
| bool operator ==(Object other) => |
| other is UniqueSymbolSizeRecord && |
| other.name == name && |
| other.tally == tally && |
| const ListEquality().equals(other.categories, categories) && |
| const ListEquality().equals(other.rustCrates, rustCrates); |
| |
| @override |
| int get hashCode => |
| name.hashCode ^ |
| tally.hashCode ^ |
| const ListEquality().hash(categories) ^ |
| const ListEquality().hash(rustCrates); |
| |
| @override |
| String toString() => 'UniqueSymbolSizeRecord(name: \'$name\', tally: $tally' |
| ', categories: $categories, rustCrates: $rustCrates)'; |
| } |