public static function fromString(string $string): self
    {
        $string = new Str($string);
        $pattern = sprintf(
            '~%s/[\w\-.]+(\+\w+)?([;,] [\w\-.]+=[\w\-.]+)?~',
            self::topLevels()->join('|')
        );

        if (!$string->matches($pattern)) {
            throw new InvalidMediaTypeString;
        }

        $splits = $string->pregSplit('~[;,] ?~');
        $matches = $splits
            ->get(0)
            ->capture(sprintf(
                '~^(?<topLevel>%s)/(?<subType>[\w\-.]+)(\+(?<suffix>\w+))?$~',
                self::topLevels()->join('|')
            ));

        $topLevel = $matches->get('topLevel');
        $subType = $matches->get('subType');
        $suffix = $matches->contains('suffix') ? $matches->get('suffix') : '';
        $params = new Map('string', Parameter::class);

        $splits
            ->drop(1)
            ->foreach(function(Str $param) use (&$params) {
                $matches = $param->capture(
                    '~^(?<key>[\w\-.]+)=(?<value>[\w\-.]+)$~'
                );

                $params = $params->put(
                    (string) $matches->get('key'),
                    new Parameter\Parameter(
                        (string) $matches->get('key'),
                        (string) $matches->get('value')
                    )
                );
            });

        return new self(
            (string) $topLevel,
            (string) $subType,
            (string) $suffix,
            $params
        );
    }