content: // Copyright 2019 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'dart:collection';
  
  import 'package:collection/collection.dart';
  
  import '../ast/sass/statement/mixin_rule.dart';
  import '../callable.dart';
  import '../deprecation.dart';
  import '../evaluation_context.dart';
  import '../util/map.dart';
  import '../value.dart';
  import '../visitor/serialize.dart';
  
  /// Feature names supported by Dart sass.
  final _features = {
    "global-variable-shadowing",
    "extend-selector-pseudoclass",
    "units-level-3",
    "at-error",
    "custom-property",
  };
  
  /// Sass introspection functions that exist as both global functions and in the
  /// `sass:meta` module that do not require access to context that's only
  /// available at runtime.
  ///
  /// Additional functions are defined in the evaluator.
  final _shared = UnmodifiableListView([
    // This is only a partial list of meta functions. The rest are defined in the
    // evaluator, because they need access to context that's only available at
    // runtime.
    _function("feature-exists", r"$feature", (arguments) {
      warnForDeprecation(
        "The feature-exists() function is deprecated.\n\n"
        "More info: https://sass-lang.com/d/feature-exists",
        Deprecation.featureExists,
      );
      var feature = arguments[0].assertString("feature");
      return SassBoolean(_features.contains(feature.text));
    }),
  
    _function(
      "inspect",
      r"$value",
      (arguments) => SassString(
        serializeValue(arguments.first, inspect: true),
        quotes: false,
      ),
    ),
  
    _function(
      "type-of",
      r"$value",
      (arguments) => SassString(
          switch (arguments[0]) {
            SassArgumentList() => "arglist",
            SassBoolean() => "bool",
            SassColor() => "color",
            SassList() => "list",
            SassMap() => "map",
            sassNull => "null",
            SassNumber() => "number",
            SassFunction() => "function",
            SassMixin() => "mixin",
            SassCalculation() => "calculation",
            SassString() => "string",
            _ => throw "[BUG] Unknown value type ${arguments[0]}",
          },
          quotes: false),
    ),
    _function("keywords", r"$args", (arguments) {
      if (arguments[0] case SassArgumentList(:var keywords)) {
        return SassMap({
          for (var (key, value) in keywords.pairs)
            SassString(key, quotes: false): value,
        });
      } else {
        throw "\$args: ${arguments[0]} is not an argument list.";
      }
    }),
  ]);
  
  /// The global definitions of Sass introspection functions.
  final global = UnmodifiableListView([
    for (var function in _shared) function.withDeprecationWarning('meta'),
  ]);
  
  /// The versions of Sass introspection functions defined in the `sass:meta`
  /// module.
  final moduleFunctions = UnmodifiableListView([
    ..._shared,
    _function("calc-name", r"$calc", (arguments) {
      var calculation = arguments[0].assertCalculation("calc");
      return SassString(calculation.name);
    }),
    _function("calc-args", r"$calc", (arguments) {
      var calculation = arguments[0].assertCalculation("calc");
      return SassList(
        calculation.arguments.map(
          (argument) => argument is Value
              ? argument
              : SassString(argument.toString(), quotes: false),
        ),
        ListSeparator.comma,
      );
    }),
    _function("accepts-content", r"$mixin", (arguments) {
      var mixin = arguments[0].assertMixin("mixin");
      return SassBoolean(switch (mixin.callable) {
        AsyncBuiltInCallable(acceptsContent: var acceptsContent) ||
        BuiltInCallable(acceptsContent: var acceptsContent) =>
          acceptsContent,
        UserDefinedCallable(declaration: MixinRule(hasContent: var hasContent)) =>
          hasContent,
        _ => throw UnsupportedError("Unknown callable type $mixin."),
      });
    }),
  ]);
  
  /// Like [BuiltInCallable.function], but always sets the URL to `sass:meta`.
  BuiltInCallable _function(
    String name,
    String arguments,
    Value callback(List<Value> arguments),
  ) =>
      BuiltInCallable.function(name, arguments, callback, url: "sass:meta");
  