"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const __1 = require("..");
const fn_1 = require("../cloudformation/fn");
/**
 * An Amazon Resource Name (ARN).
 * http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
 */
class Arn extends __1.Token {
    /**
     * Creates an ARN from components.
     * If any component is the empty string,
     * an empty string will be inserted into the generated ARN
     * at the location that component corresponds to.
     */
    static fromComponents(components) {
        const partition = components.partition == null
            ? new __1.AwsPartition()
            : components.partition;
        const region = components.region == null
            ? new __1.AwsRegion()
            : components.region;
        const account = components.account == null
            ? new __1.AwsAccountId()
            : components.account;
        const values = ['arn', ':', partition, ':', components.service, ':', region, ':', account, ':', components.resource];
        const sep = components.sep || '/';
        if (sep !== '/' && sep !== ':') {
            throw new Error('resourcePathSep may only be ":" or "/"');
        }
        if (components.resourceName != null) {
            values.push(sep);
            values.push(components.resourceName);
        }
        return new Arn(new __1.FnConcat(...values));
    }
    /**
     * Given an ARN, parses it and returns components.
     *
     * The ARN it will be parsed and validated. The separator (`sep`) will be
     * set to '/' if the 6th component includes a '/', in which case, `resource`
     * will be set to the value before the '/' and `resourceName` will be the
     * rest. In case there is no '/', `resource` will be set to the 6th
     * components and `resourceName` will be set to the rest of the string.
     *
     * @returns an ArnComponents object which allows access to the various
     *          components of the ARN.
     */
    static parse(arn) {
        const components = arn.split(':');
        if (components.length < 6) {
            throw new Error('ARNs must have at least 6 components: ' + arn);
        }
        const [arnPrefix, partition, service, region, account, sixth, ...rest] = components;
        if (arnPrefix !== 'arn') {
            throw new Error('ARNs must start with "arn:": ' + arn);
        }
        if (!service) {
            throw new Error('The `service` component (3rd component) is required: ' + arn);
        }
        if (!sixth) {
            throw new Error('The `resource` component (6th component) is required: ' + arn);
        }
        let resource;
        let resourceName;
        let sep;
        let sepIndex = sixth.indexOf('/');
        if (sepIndex !== -1) {
            sep = '/';
        }
        else if (rest.length > 0) {
            sep = ':';
            sepIndex = -1;
        }
        if (sepIndex !== -1) {
            resource = sixth.substr(0, sepIndex);
            resourceName = sixth.substr(sepIndex + 1);
        }
        else {
            resource = sixth;
        }
        if (rest.length > 0) {
            if (!resourceName) {
                resourceName = '';
            }
            else {
                resourceName += ':';
            }
            resourceName += rest.join(':');
        }
        const result = { service, resource };
        if (partition) {
            result.partition = partition;
        }
        if (region) {
            result.region = region;
        }
        if (account) {
            result.account = account;
        }
        if (resourceName) {
            result.resourceName = resourceName;
        }
        if (sep) {
            result.sep = sep;
        }
        return result;
    }
    /**
     * Given a Token evaluating to ARN, parses it and returns components.
     *
     * The ARN cannot be validated, since we don't have the actual value yet
     * at the time of this function call. You will have to know the separator
     * and the type of ARN.
     *
     * The resulting `ArnComponents` object will contain tokens for the
     * subexpressions of the ARN, not string literals.
     *
     * WARNING: this function cannot properly parse the complete final
     * resourceName (path) out of ARNs that use '/' to both separate the
     * 'resource' from the 'resourceName' AND to subdivide the resourceName
     * further. For example, in S3 ARNs:
     *
     *      arn:aws:s3:::my_corporate_bucket/path/to/exampleobject.png
     *
     * After parsing the resourceName will not contain 'path/to/exampleobject.png'
     * but simply 'path'. This is a limitation because there is no slicing
     * functionality in CloudFormation templates.
     *
     * @param arn The input token that contains an ARN
     * @param sep The separator used to separate resource from resourceName
     * @param hasName Whether there is a name component in the ARN at all.
     * For example, SNS Topics ARNs have the 'resource' component contain the
     * topic name, and no 'resourceName' component.
     * @returns an ArnComponents object which allows access to the various
     * components of the ARN.
     */
    static parseToken(arn, sep = '/', hasName = true) {
        // Arn ARN looks like:
        // arn:partition:service:region:account-id:resource
        // arn:partition:service:region:account-id:resourcetype/resource
        // arn:partition:service:region:account-id:resourcetype:resource
        // We need the 'hasName' argument because {Fn::Select}ing a nonexistent field
        // throws an error.
        const components = new fn_1.FnSplit(':', arn);
        const partition = new fn_1.FnSelect(1, components);
        const service = new fn_1.FnSelect(2, components);
        const region = new fn_1.FnSelect(3, components);
        const account = new fn_1.FnSelect(4, components);
        if (sep === ':') {
            const resource = new fn_1.FnSelect(5, components);
            const resourceName = hasName ? new fn_1.FnSelect(6, components) : undefined;
            return { partition, service, region, account, resource, resourceName, sep };
        }
        else {
            const lastComponents = new fn_1.FnSplit(sep, new fn_1.FnSelect(5, components));
            const resource = new fn_1.FnSelect(0, lastComponents);
            const resourceName = hasName ? new fn_1.FnSelect(1, lastComponents) : undefined;
            return { partition, service, region, account, resource, resourceName, sep };
        }
    }
}
exports.Arn = Arn;
//# sourceMappingURL=data:application/json;base64, 