A small menubar app that allows you to switch between R versions quickly (if you have multiple versions of R framework installed).
https://rud.is/rswitch
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.9 KiB
123 lines
3.9 KiB
//
|
|
// RUtils.swift
|
|
// RSwitch
|
|
//
|
|
// Created by hrbrmstr on 9/1/19.
|
|
// Copyright © 2019 Bob Rudis. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Cocoa
|
|
|
|
struct versionInfo {
|
|
var path : String
|
|
var current: Bool
|
|
}
|
|
|
|
class RVersions {
|
|
|
|
static let macos_r_framework = "/Library/Frameworks/R.framework/Versions" // Where the official R installs go
|
|
|
|
static let replRegex = try!NSRegularExpression(pattern: "[[:alpha:][:space:]\"#_]+", options: NSRegularExpression.Options.caseInsensitive)
|
|
|
|
static func currentVersionTarget() -> String {
|
|
|
|
// get where Current points to
|
|
let furl = NSURL(fileURLWithPath: (RVersions.macos_r_framework as NSString).appendingPathComponent("Current"))
|
|
|
|
if (furl.fileReferenceURL() != nil) {
|
|
do {
|
|
let fdat = try NSURL(resolvingAliasFileAt: furl as URL, options: [])
|
|
return(fdat.lastPathComponent!)
|
|
} catch {
|
|
return(furl.path!)
|
|
}
|
|
} else {
|
|
return("")
|
|
}
|
|
|
|
}
|
|
|
|
static func preciseVersion(versionPath : String) -> String {
|
|
|
|
let actualPath = NSString.path(withComponents: [ versionPath.starts(with: "/") ? "" : RVersions.macos_r_framework, versionPath, "Headers", "Rversion.h" ])
|
|
|
|
var out = ""
|
|
|
|
if (FileManager.default.fileExists(atPath:actualPath)) {
|
|
|
|
do {
|
|
|
|
let versionHeader = (try NSString(contentsOfFile: actualPath, encoding: String.Encoding.utf8.rawValue)) as String
|
|
|
|
let majMin = versionHeader
|
|
.split(separator: "\n")
|
|
.filter{
|
|
$0.contains("R_MAJOR") || $0.contains("R_MINOR")
|
|
}
|
|
.map{
|
|
replRegex.stringByReplacingMatches(in: String($0),
|
|
options: [],
|
|
range: NSMakeRange(0, $0.count),
|
|
withTemplate: "")
|
|
}
|
|
|
|
out = " (" + majMin[0] + "." + majMin[1] + ")"
|
|
|
|
} catch {
|
|
}
|
|
|
|
}
|
|
|
|
return(out)
|
|
|
|
}
|
|
|
|
static func hasRBinary(versionPath : String) -> Bool {
|
|
let resourcesPath = NSString.path(withComponents: [ versionPath.starts(with: "/") ? "" : RVersions.macos_r_framework, versionPath, "Resources", "bin", "R" ])
|
|
return(FileManager.default.fileExists(atPath: resourcesPath))
|
|
}
|
|
|
|
static func reloadVersions() throws -> [String] {
|
|
|
|
// gets a directory listing
|
|
let entries = try FileManager.default.contentsOfDirectory(atPath: RVersions.macos_r_framework)
|
|
|
|
// retrieves all versions (excludes hidden files and the Current alias
|
|
return(entries.sorted().filter { !($0.hasPrefix(".")) && !($0 == "Current") })
|
|
|
|
}
|
|
|
|
static func populateRVersionsMenu(menu : NSMenu, handler : Selector) {
|
|
|
|
do {
|
|
|
|
let targetPath = RVersions.currentVersionTarget()
|
|
let versions = try RVersions.reloadVersions()
|
|
|
|
// populate menu items with all installed R versions, ensuring we
|
|
// put a checkbox next to the one that is Current
|
|
|
|
var i = 1
|
|
for version in versions {
|
|
let complete = RVersions.hasRBinary(versionPath: version)
|
|
let keynum = (i < 10) ? String(i) : ""
|
|
let item = NSMenuItem(
|
|
title: complete ? version + RVersions.preciseVersion(versionPath: version) : version + " (incomplete)",
|
|
action: complete ? handler : nil,
|
|
keyEquivalent: complete ? keynum : ""
|
|
)
|
|
item.isEnabled = complete
|
|
if (version == targetPath) { item.state = NSControl.StateValue.on }
|
|
item.representedObject = version
|
|
menu.addItem(item)
|
|
i = complete ? i + 1 : i
|
|
}
|
|
|
|
} catch {
|
|
AppAlerts.quitAlert("Failed to list contents of R framework directory. You either do not have R installed or have incorrect permissions set on " + RVersions.macos_r_framework)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|