# -*-eselect-*- vim: ft=eselect # Copyright 2005-2024 Gentoo Authors # Distributed under the terms of the GNU GPL version 2 or later DESCRIPTION="Manage the Swift symlink" MAINTAINER="itai@itaiferber.net" VERSION="1.0" inherit path-manipulation BIN_DIR="${EROOT%/}/usr/bin" ### Utility Functions ### # @FUNCTION: get_index # @USAGE: ... # @DESCRIPTION: # Returns the index of an element in an array, or the empty string if the # element was not found. get_index() { local element="$1" shift local i elements=( "$@" ) for i in "${!elements[@]}"; do if [[ "${elements[i]}" = "${element}" ]]; then echo "${i}" return 0 fi done return 1 } # @FUNCTION: sort_swift_targets # @USAGE: ... # @DESCRIPTION: # Lexicographically sorts an array of Swift target identifiers. sort_swift_targets() { # `sort` may not support '--version-sort', so fall back on error local vsort="sort --version-sort" ${vsort} /dev/null || vsort="sort" # Stolen from `app-eselect/eselect-luajit`: to handle potential `_beta` # version suffixes, we: # 1. Turn `swift-x.y.z` into `x.y.z 1 swift-x.y.z` # 2. Turn `swift-x.y.z_beta...` into `x.y.z 0 swift-x.y.z_beta...` # 3. Sort, which moves `_beta` versions before corresponding non-beta # versions # 4. Remove leading trivia printf "%s\n" "$@" \ | sed -e 's/^\(swift-\)\?\([[:digit:].]\+\)[-_]beta/\2 0 &/' \ -e 't;s/^\(swift-\)\?\([[:digit:].]\+\)/\2 1 &/' \ | LC_ALL=C ${vsort} \ | sed 's/.* //' } # @FUNCTION: list_targets # @DESCRIPTION: # Returns a lexicographically-sorted list of Swift targets found in `BIN_DIR`. list_targets() { local identifiers mapfile -t identifiers < <(find "${BIN_DIR}" -maxdepth 1 -type l -iname 'swift-[[:digit:]]*' -exec basename '{}' ';') sort_swift_targets "${identifiers[@]}" } # @FUNCTION: list_target_links # @USAGE: # @DESCRIPTION: # Given a Swift target identifier, lists all of the symlinks found in `BIN_DIR` # that correspond to that Swift target. list_target_links() { local target="$1" local version_number="${target#swift-}" local swift_dir="$(dirname "$(canonicalise "${BIN_DIR}/${target}")")" local files mapfile -t files < <(find "${BIN_DIR}" -maxdepth 1 -type l -iname "*-${version_number}") for f in "${files[@]}"; do local d="$(dirname "$(canonicalise "${f}")")" if [[ "${d}" == "${swift_dir}"* ]]; then echo "${f}" fi done } # @FUNCTION: current_target_index # @USAGE: ... # @DESCRIPTION: # Returns the index of the currently-set target from the given list of available # Swift targets. If `/usr/bin/swift` does not point to any of the given Swift # targets, returns an empty string. current_target_index() { local t canonical_targets=() for t in "$@"; do canonical_targets+=("$(canonicalise "${BIN_DIR}/${t}")") done local canonical_swift_path="$(canonicalise "${BIN_DIR}/swift")" get_index "${canonical_swift_path}" "${canonical_targets[@]}" } # @FUNCTION: current_target # @USAGE: ... # @DESCRIPTION: # Returns the target identifier of the currently-set target from the given list # of available Swift targets. If `/usr/bin/swift` does not point to any of the # given Swift targets, returns an empty string. current_target() { local targets=( "$@" ) local target="$(current_target_index "${targets[@]}")" if is_number "${target}"; then echo "${targets[target]}" else return 1 fi } # @FUNCTION: unset_target # @USAGE: # @DESCRIPTION: # Unsets any unversioned links in `BIN_DIR` if they point to the given Swift # target identifier. unset_target() { local target="$1" local version_number="${target#swift-}" local links mapfile -t links < <(list_target_links "${target}") local link for link in "${links[@]}"; do local unversioned_link="${link%-"${version_number}"}" if [[ "$(canonicalise "${link}")" = "$(canonicalise "${unversioned_link}")" ]]; then rm "${unversioned_link}" || die -q "Couldn't remove symlink '${unversioned_link}'" fi done } # @FUNCTION: set_target # @USAGE: ... # @DESCRIPTION: # Unsets the current Swift target and sets unversioned symlinks in `BIN_DIR` for # the given Swift target identifier. Does nothing if the given target identifier # is the same as the current target identifier. set_target() { local target="$1" shift local current_target="$(current_target "$@")" if [[ "${target}" = "${current_target}" ]]; then return fi unset_target "${current_target}" local version_number="${target#swift-}" local links mapfile -t links < <(list_target_links "${target}") local link for link in "${links[@]}"; do local unversioned_link="${link%-"${version_number}"}" ln -fs "${link}" "${unversioned_link}" || die -q "Couldn't create symlink '${unversioned_link}'" done } ### List Action ### describe_list() { echo "Lists available Swift versions" } do_list() { local targets mapfile -t targets < <(list_targets) if [[ -n "${targets}" ]]; then local current_target="$(current_target_index "${targets[@]}")" if is_number "${current_target}"; then targets[current_target]="$(highlight_marker "${targets[current_target]}")" fi write_list_start "Available Swift versions:" write_numbered_list "${targets[@]}" else write_list_start "No available Swift versions" fi } ### Show Action ### describe_show() { echo "Show the current Swift implementation" } do_show() { local targets mapfile -t targets < <(list_targets) if [[ -n "${targets[@]}" ]]; then write_list_start "Current Swift implementation:" local target="$(current_target "${targets[@]}")" write_kv_list_entry "${target:-(unset)}" else write_list_start "No available Swift versions" fi } ### Set Action ### describe_set() { echo "Set active Swift version" } describe_set_parameters() { echo "" } describe_set_options() { echo "target : Target number or name (from 'list')" } do_set() { [[ $# -eq 0 || -z "$1" ]] && die -q "No Swift version specified" [[ $# -gt 1 ]] && die -q "'set' takes only one parameter" local targets mapfile -t targets < <(list_targets) local target="$1" if is_number "${target}"; then [[ "$target" -gt 0 && "$1" -le "${#targets[@]}" ]] || die -q "'$1' is not a valid target" target="${targets[target-1]}" else target_index="$(get_index "$1" "${targets[@]}")" is_number "${target_index}" || die -q "'$1' is not a valid target" fi set_target "${target}" "${targets[@]}" } ### Unset Action ### describe_unset() { echo "Unsets any active Swift version" } do_unset() { [[ $# -eq 0 ]] || die -q "'unset' does not take any parameters" local targets mapfile -t targets < <(list_targets) local target="$(current_target "${targets[@]}")" unset_target "${target}" } ### Update Action ### describe_update() { echo "Switch to the most recent Swift version" } do_update() { [[ $# -eq 0 ]] || die -q "'update' does not take any parameters" local targets mapfile -t targets < <(list_targets) [[ "${#targets[@]}" -gt 0 ]] || die -q "No Swift versions found" local target="$(current_target "${targets[@]}")" if [[ -n "${target}" ]]; then unset_target "${target}" fi local new_target="${targets[-1]}" set_target "${new_target}" "${targets[@]}" }