blob: 7de30cb2a891ed146ed2a8ea7dad6f067fd0de9f [file] [log] [blame]
// 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)';
}