Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the relative path usage instead absolute path in symlink target URL #4

Merged
merged 4 commits into from
Aug 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

- Refactor application.

## [0.7.0] - 2021-08-22

### Added

- Added new classes `Path` for working with path's and creating relative path from specified directory.
- Add unit-tests cases for the `Path` class.

### Changed

- Change soft link creation using relative instead absolute path.

## [0.6.0] - 2021-08-02

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The Quick Symlink is a `Finder extension` which provides a `contextual menu item` for the symbolic links creation on macOS.

[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_07,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_20,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date)
[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date)

[![license](https://img.shields.io/badge/license-MIT-informational?style=flat-square)](LICENSE) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg?style=flat-square)](CODE_OF_CONDUCT.md)

Expand Down
5 changes: 3 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
- [x] Develop the `Finder extension` which allows to create a symlinks for selected folders and files via contextual menu.
- [x] Add the new menu item for replacing selected folders and files with symlinks.
- [x] Add the new menu item for creating symlink in a parent directory (parent for target objects).
- [ ] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume)
- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu.
- [x] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume)
- [ ] Refactor code in `commons/*` part and add unit-tests
- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu
- [ ] Develop the action panel for created symbolic links and hard lonks (in the app window):
- [ ] a) (if broken) process to browse finder for 'Find/fix missing target'
- [ ] b) Modify existing paths and symbolic link features
Expand Down
20 changes: 20 additions & 0 deletions commons/FileSystem/Path.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Path.swift
// quick-symlink
//
// Created by Alexander A. Kropotin on 22/08/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import Foundation

public protocol Path {

func relativize(to other: Path!) -> Path!;

func getPathFragments() -> [String]!;

func toUrl() -> URL?;

func toUriString() -> String?;
}
82 changes: 82 additions & 0 deletions commons/FileSystem/ResourcePath.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// ResourcePath.swift
// quick-symlink
//
// Created by Alexander A. Kropotin on 22/08/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import Foundation

public class ResourcePath: Path {

public static func of(url: URL!) -> Path! {
return ResourcePath.of(fragments: url.pathComponents)
}

public static func of(fragments: [String]!) -> Path! {
return ResourcePath.init(of: fragments);
}

private var uriFragments: [String]!;

public init(of fragments: [String]) {
self.uriFragments = fragments;
}

public func getPathFragments() -> [String]! {
return self.uriFragments;
}

public func relativize(to other: Path!) -> Path! {
var pathFragments = self.uriFragments;
var targetPathFragments = other.getPathFragments();

var destinationPath = URL.init(string: "./")!;
for targetPathFragment in targetPathFragments! {
if (!(pathFragments?.contains(targetPathFragment))!) {
break;
}

pathFragments?.remove(at: 0);
targetPathFragments?.remove(at: 0);
}

for _ in targetPathFragments! {
destinationPath.appendPathComponent("../");
}

for pathFragment in pathFragments! {
destinationPath.appendPathComponent(pathFragment);
}

//pathFragments!.append(contentsOf: targetPathFragments!);

return ResourcePath.of(url: destinationPath);
}

public func toUrl() -> URL? {
if self.uriFragments.count == 0 {
return nil;
}

var uri = URL.init(string: self.uriFragments.first!)!;
for fragment in self.uriFragments.dropFirst().dropLast() {
uri.appendPathComponent(fragment);
uri.appendPathComponent("/");
}
uri.appendPathComponent(self.uriFragments.last!);

return uri;
}

public func toUriString() -> String? {
if self.uriFragments.count == 0 {
return nil;
}

return self.toUrl()!.absoluteString;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class CreateLinkAction: QuickSymlinkAction {
let targetPath = self.getTargetPath(path, to: path.deletingLastPathComponent());

do {
try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: path);
try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: path).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!);
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class PasteLinkAction: QuickSymlinkAction {
let targetPath = self.getTargetPath(pathUrl, to: target);

do {
try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: URL(fileURLWithPath: path));
try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: pathUrl).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!);
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class ReplaceWithLinkAction: QuickSymlinkAction {

do {
try FileManager.default.moveItem(at: pathUrl, to: targetPath!);
try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: targetPath!);
try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: ResourcePath.of(url: targetPath).relativize(to: ResourcePath.of(url: pathUrl.deletingLastPathComponent())).toUrl()!);
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
Expand Down
22 changes: 22 additions & 0 deletions quick-symlink-tests/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
49 changes: 49 additions & 0 deletions quick-symlink-tests/ResourcePathTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// ResourcePathTest.swift
// quick-symlink-tests
//
// Created by Alexander A. Kropotin on 22/08/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import XCTest

class ResourcePathTest: XCTestCase {

func test_toURL_methodExecution_returnedURLIsEqualToInitialURL() {
let uri: URL = URL.init(string: "/a/b/c/d")!;
let path: Path = ResourcePath.of(url: uri);

XCTAssert(path.toUrl() == uri, "The Path URL is not equal to URL");
}

func test_relativize_whenCurrentDirectoryIsNestedToOtherDirectory_thenReturnPathStartingFromOtherDirectory() {
let currentUri: URL = URL.init(string: "/a/b/c/d")!;
let otherUri: URL = URL.init(string: "/a/b")!;

let relativePath: Path = ResourcePath.of(url: currentUri)
.relativize(to: ResourcePath.of(url: otherUri));

XCTAssert(relativePath.toUriString() == "./c/d", "The relative path is wrong");
}

func test_relativize_whenOtherDirectoryIsNestedToCurrentDirectory_thenReturnPathWithOnlyJumpsAboveOtherDirectory() {
let currentUri: URL = URL.init(string: "/a/b")!;
let otherUri: URL = URL.init(string: "/a/b/c/d")!;

let relativePath: Path = ResourcePath.of(url: currentUri)
.relativize(to: ResourcePath.of(url: otherUri));

XCTAssert(relativePath.toUriString() == "./../..", "The relative path is wrong");
}

func test_relativize_whenCurrentDirectoryAndOtherDirectoryAreNestedToGeneralDirectoryy_thenReturnPathWithJumpsAboveOtherDirectoryAndPartOfCurrentDirectory() {
let currentUri: URL = URL.init(string: "/a/b/c1/d1")!;
let otherUri: URL = URL.init(string: "/a/b/c2/d2")!;

let relativePath: Path = ResourcePath.of(url: currentUri)
.relativize(to: ResourcePath.of(url: otherUri));

XCTAssert(relativePath.toUriString() == "./../../c1/d1", "The relative path is wrong");
}
}
Loading