//
// R U t i l s . s w i f t
// R S w i t c h
//
// C r e a t e d b y h r b r m s t r o n 9 / 1 / 1 9 .
// C o p y r i g h t © 2 0 1 9 B o b R u d i s . A l l r i g h t s r e s e r v e d .
//
import Foundation
import Cocoa
struct versionInfo {
var path : String
var current : Bool
}
class RVersions {
static let macos_r_framework = " /Library/Frameworks/R.framework/Versions " // W h e r e t h e o f f i c i a l R i n s t a l l s g o
static let replRegex = try ! NSRegularExpression ( pattern : " [[:alpha:][:space:] \" #_]+ " , options : NSRegularExpression . Options . caseInsensitive )
static func currentVersionTarget ( ) -> String {
// g e t w h e r e C u r r e n t p o i n t s t o
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 ] {
// g e t s a d i r e c t o r y l i s t i n g
let entries = try FileManager . default . contentsOfDirectory ( atPath : RVersions . macos_r_framework )
// r e t r i e v e s a l l v e r s i o n s ( e x c l u d e s h i d d e n f i l e s a n d t h e C u r r e n t a l i a s
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 ( )
// p o p u l a t e m e n u i t e m s w i t h a l l i n s t a l l e d R v e r s i o n s , e n s u r i n g w e
// p u t a c h e c k b o x n e x t t o t h e o n e t h a t i s C u r r e n t
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 )
}
}
}