toCanonicalMap method

Map<String, Object?> toCanonicalMap()

A stable, JSON-encodable canonical form of the whole spec: keys sorted at every level, lists sorted (semantically orderless), durations as seconds, null fields omitted. Two specs that parse to the same policy always produce an equal map — this is the content-addressed cache key input.

Implementation

Map<String, Object?> toCanonicalMap() {
  return {
    'apiVersion': 'fa/v1',
    'kind': 'Cube',
    'metadata': {
      if (description != null) 'description': description,
      'name': name,
    },
    'spec': {
      'backend': backend.name,
      // Omitted when false so existing specs keep byte-identical cache
      // keys.
      if (allowDegrade) 'allowDegrade': true,
      'cache': {
        'enabled': cache.enabled,
        if (cache.paths.isNotEmpty) 'paths': [...cache.paths]..sort(),
        'restore': cache.restore,
        if (cache.ttl != null) 'ttl': cache.ttl!.inSeconds,
      },
      'env': [
        for (final variable in [...env.vars]..sort(_byName))
          switch (variable) {
            CubeEnvValue(:final name, :final value) => {
              'name': name,
              'value': value,
            },
            CubeEnvValueFrom(:final name, :final source) => {
              'name': name,
              'valueFrom': source,
            },
            CubeEnvHidden(:final name) => {'hidden': true, 'name': name},
          },
      ],
      'filesystem': {
        'mounts': [
          for (final mount in [...filesystem.mounts]..sort(_byPath))
            {'access': mount.access.label, 'path': mount.path},
        ],
        'workspace': filesystem.workspace,
      },
      'network': {
        'allow': [
          for (final rule in [...network.allow]..sort(_byHost))
            _canonicalRule(rule),
        ],
        'deny': [
          for (final rule in [...network.deny]..sort(_byHost))
            _canonicalRule(rule),
        ],
      },
      'resources': {
        if (resources.cpu != null) 'cpu': resources.cpu,
        if (resources.diskBytes != null) 'diskBytes': resources.diskBytes,
        if (resources.memoryBytes != null)
          'memoryBytes': resources.memoryBytes,
        if (resources.timeout != null)
          'timeout': resources.timeout!.inSeconds,
      },
      'tools': {
        'allow': [...tools.allow]..sort(),
        'deny': [...tools.deny]..sort(),
      },
    },
  };
}