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

//
// 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)
}
}
}