diff --git a/.gitignore b/.gitignore index 212de44..d63f5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -.DS_Store \ No newline at end of file +.DS_Store +*.xcuserdatad \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index dec5a5a..6a82117 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,7 @@ serde = { version = "1", features = ["derive"] } clap = { version = "3.1.18", features = ["derive"] } anyhow = "1.0" -itertools = "0.10.3" \ No newline at end of file +itertools = "0.10.3" + +[lib] +crate-type = ["cdylib", "staticlib", "lib"] diff --git a/FastCoster/CosterRs/coster-bridge.h b/FastCoster/CosterRs/coster-bridge.h new file mode 100644 index 0000000..1df9e72 --- /dev/null +++ b/FastCoster/CosterRs/coster-bridge.h @@ -0,0 +1,13 @@ +// +// coster-bridge.h +// FastCoster +// +// Created by Michael Pivato on 3/2/2023. +// + +#ifndef coster_bridge_h +#define coster_bridge_h + +#import "libcoster_rs.h" + +#endif /* coster_bridge_h */ diff --git a/FastCoster/CosterRs/create-lib.sh b/FastCoster/CosterRs/create-lib.sh new file mode 100644 index 0000000..c988997 --- /dev/null +++ b/FastCoster/CosterRs/create-lib.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# create-lib.sh +# FastCoster +# +# Created by Michael Pivato on 3/2/2023. +# + +set -x +# create-iospw.sh +# Build the correct Rust target and place +# the resulting library in the build products +# +# The $PATH used by Xcode likely won't contain Cargo, fix that. +# In addition, the $PATH used by XCode has lots of Apple-specific +# developer tools that your Cargo isn't expecting to use, fix that. +# Note: This assumes a default `rustup` setup and default path. +build_path="$HOME/.cargo/bin:/usr/local/bin:/usr/bin:/bin" +# +# Figure out the correct Rust target from the ARCHS and PLATFORM. +# This script expects just one element in ARCHS. +case "$ARCHS" in + "arm64") rust_arch="aarch64" ;; + "x86_64") rust_arch="x86_64" ;; + *) echo "error: unsupported architecture: $ARCHS" ;; +esac +if [[ "$PLATFORM_NAME" == "macosx" ]]; then + rust_platform="apple-darwin" +else + rust_platform="apple-ios" +fi +if [[ "$PLATFORM_NAME" == "iphonesimulator" ]]; then + if [[ "${rust_arch}" == "aarch64" ]]; then + rust_abi="-sim" + else + rust_abi="" + fi +else + rust_abi="" +fi +rust_target="${rust_arch}-${rust_platform}${rust_abi}" +# +# Build library in debug or release +build_args=(--manifest-path ../costerrs/Cargo.toml --target "${rust_target}") +if [[ "$CONFIGURATION" == "Release" ]]; then + rust_config="release" + env PATH="${build_path}" cargo build --release "${build_args[@]}" +elif [[ "$CONFIGURATION" == "Debug" ]]; then + rust_config="release" + env PATH="${build_path}" cargo build --release "${build_args[@]}" +else + echo "error: Unexpected build configuration: $CONFIGURATION" +fi +# +# Copy the built library to the derived files directory +cp -v "../costerrs/target/${rust_target}/${rust_config}/libcoster_rs.a" ${DERIVED_FILES_DIR} diff --git a/FastCoster/CosterRs/libcoster_rs.h b/FastCoster/CosterRs/libcoster_rs.h new file mode 100644 index 0000000..a55b294 --- /dev/null +++ b/FastCoster/CosterRs/libcoster_rs.h @@ -0,0 +1,8 @@ +#include +#include +#include +#include + +char *move_money_from_text(const char *rules, const char *lines, bool use_numeric_accounts); + +void move_money_from_text_free(char *s); diff --git a/FastCoster/CosterRs/remove-lib.sh b/FastCoster/CosterRs/remove-lib.sh new file mode 100644 index 0000000..321ae33 --- /dev/null +++ b/FastCoster/CosterRs/remove-lib.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -x +# delete-iospw.sh +# keyring-tester +# +# Created by Daniel Brotsky on 1/1/22. +# +rm -fv ${DERIVED_FILES_DIR}/libcoster_rs.a diff --git a/FastCoster/FastCoster.xcodeproj/project.pbxproj b/FastCoster/FastCoster.xcodeproj/project.pbxproj new file mode 100644 index 0000000..863333e --- /dev/null +++ b/FastCoster/FastCoster.xcodeproj/project.pbxproj @@ -0,0 +1,698 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 5A450751298CE6D500E3D402 /* CsvDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A450750298CE6D500E3D402 /* CsvDocument.swift */; }; + 5A45075B298D01EF00E3D402 /* libcoster_rs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A45075A298D01EF00E3D402 /* libcoster_rs.a */; }; + 5ADD9F2D298A713300F998F5 /* FastCosterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADD9F2C298A713300F998F5 /* FastCosterApp.swift */; }; + 5ADD9F2F298A713300F998F5 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADD9F2E298A713300F998F5 /* ContentView.swift */; }; + 5ADD9F31298A713400F998F5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5ADD9F30298A713400F998F5 /* Assets.xcassets */; }; + 5ADD9F35298A713400F998F5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5ADD9F34298A713400F998F5 /* Preview Assets.xcassets */; }; + 5ADD9F3F298A713400F998F5 /* FastCosterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADD9F3E298A713400F998F5 /* FastCosterTests.swift */; }; + 5ADD9F49298A713400F998F5 /* FastCosterUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADD9F48298A713400F998F5 /* FastCosterUITests.swift */; }; + 5ADD9F4B298A713400F998F5 /* FastCosterUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADD9F4A298A713400F998F5 /* FastCosterUITestsLaunchTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 5ADD9F3B298A713400F998F5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5ADD9F21298A713300F998F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5ADD9F28298A713300F998F5; + remoteInfo = FastCoster; + }; + 5ADD9F45298A713400F998F5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5ADD9F21298A713300F998F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5ADD9F28298A713300F998F5; + remoteInfo = FastCoster; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 5A450750298CE6D500E3D402 /* CsvDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CsvDocument.swift; sourceTree = ""; }; + 5A450754298CFFAB00E3D402 /* coster-bridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "coster-bridge.h"; sourceTree = ""; }; + 5A450755298CFFE400E3D402 /* create-lib.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "create-lib.sh"; sourceTree = ""; }; + 5A450756298D00AE00E3D402 /* remove-lib.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "remove-lib.sh"; sourceTree = ""; }; + 5A45075A298D01EF00E3D402 /* libcoster_rs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcoster_rs.a; path = "../costerrs/target/aarch64-apple-ios/release/libcoster_rs.a"; sourceTree = ""; }; + 5ACE47E6298D087B00834311 /* libcoster_rs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libcoster_rs.h; sourceTree = ""; }; + 5ADD9F29298A713300F998F5 /* FastCoster.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FastCoster.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5ADD9F2C298A713300F998F5 /* FastCosterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastCosterApp.swift; sourceTree = ""; }; + 5ADD9F2E298A713300F998F5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 5ADD9F30298A713400F998F5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 5ADD9F32298A713400F998F5 /* FastCoster.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FastCoster.entitlements; sourceTree = ""; }; + 5ADD9F34298A713400F998F5 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 5ADD9F3A298A713400F998F5 /* FastCosterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FastCosterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5ADD9F3E298A713400F998F5 /* FastCosterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastCosterTests.swift; sourceTree = ""; }; + 5ADD9F44298A713400F998F5 /* FastCosterUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FastCosterUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5ADD9F48298A713400F998F5 /* FastCosterUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastCosterUITests.swift; sourceTree = ""; }; + 5ADD9F4A298A713400F998F5 /* FastCosterUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastCosterUITestsLaunchTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5ADD9F26298A713300F998F5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5A45075B298D01EF00E3D402 /* libcoster_rs.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5ADD9F37298A713400F998F5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5ADD9F41298A713400F998F5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5A450752298CFEF600E3D402 /* CosterRs */ = { + isa = PBXGroup; + children = ( + 5A450754298CFFAB00E3D402 /* coster-bridge.h */, + 5ACE47E6298D087B00834311 /* libcoster_rs.h */, + 5A450755298CFFE400E3D402 /* create-lib.sh */, + 5A450756298D00AE00E3D402 /* remove-lib.sh */, + ); + path = CosterRs; + sourceTree = ""; + }; + 5A450759298D01EF00E3D402 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5A45075A298D01EF00E3D402 /* libcoster_rs.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5ADD9F20298A713300F998F5 = { + isa = PBXGroup; + children = ( + 5A450752298CFEF600E3D402 /* CosterRs */, + 5ADD9F2B298A713300F998F5 /* FastCoster */, + 5ADD9F3D298A713400F998F5 /* FastCosterTests */, + 5ADD9F47298A713400F998F5 /* FastCosterUITests */, + 5ADD9F2A298A713300F998F5 /* Products */, + 5A450759298D01EF00E3D402 /* Frameworks */, + ); + sourceTree = ""; + }; + 5ADD9F2A298A713300F998F5 /* Products */ = { + isa = PBXGroup; + children = ( + 5ADD9F29298A713300F998F5 /* FastCoster.app */, + 5ADD9F3A298A713400F998F5 /* FastCosterTests.xctest */, + 5ADD9F44298A713400F998F5 /* FastCosterUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 5ADD9F2B298A713300F998F5 /* FastCoster */ = { + isa = PBXGroup; + children = ( + 5ADD9F2C298A713300F998F5 /* FastCosterApp.swift */, + 5ADD9F2E298A713300F998F5 /* ContentView.swift */, + 5ADD9F30298A713400F998F5 /* Assets.xcassets */, + 5ADD9F32298A713400F998F5 /* FastCoster.entitlements */, + 5ADD9F33298A713400F998F5 /* Preview Content */, + 5A450750298CE6D500E3D402 /* CsvDocument.swift */, + ); + path = FastCoster; + sourceTree = ""; + }; + 5ADD9F33298A713400F998F5 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 5ADD9F34298A713400F998F5 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 5ADD9F3D298A713400F998F5 /* FastCosterTests */ = { + isa = PBXGroup; + children = ( + 5ADD9F3E298A713400F998F5 /* FastCosterTests.swift */, + ); + path = FastCosterTests; + sourceTree = ""; + }; + 5ADD9F47298A713400F998F5 /* FastCosterUITests */ = { + isa = PBXGroup; + children = ( + 5ADD9F48298A713400F998F5 /* FastCosterUITests.swift */, + 5ADD9F4A298A713400F998F5 /* FastCosterUITestsLaunchTests.swift */, + ); + path = FastCosterUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5ADD9F28298A713300F998F5 /* FastCoster */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5ADD9F4E298A713400F998F5 /* Build configuration list for PBXNativeTarget "FastCoster" */; + buildPhases = ( + 5A450757298D010E00E3D402 /* Create Rust Lib */, + 5ADD9F25298A713300F998F5 /* Sources */, + 5ADD9F26298A713300F998F5 /* Frameworks */, + 5ADD9F27298A713300F998F5 /* Resources */, + 5A450758298D014E00E3D402 /* Remove Rust Lib */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FastCoster; + productName = FastCoster; + productReference = 5ADD9F29298A713300F998F5 /* FastCoster.app */; + productType = "com.apple.product-type.application"; + }; + 5ADD9F39298A713400F998F5 /* FastCosterTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5ADD9F51298A713400F998F5 /* Build configuration list for PBXNativeTarget "FastCosterTests" */; + buildPhases = ( + 5ADD9F36298A713400F998F5 /* Sources */, + 5ADD9F37298A713400F998F5 /* Frameworks */, + 5ADD9F38298A713400F998F5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5ADD9F3C298A713400F998F5 /* PBXTargetDependency */, + ); + name = FastCosterTests; + productName = FastCosterTests; + productReference = 5ADD9F3A298A713400F998F5 /* FastCosterTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 5ADD9F43298A713400F998F5 /* FastCosterUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5ADD9F54298A713400F998F5 /* Build configuration list for PBXNativeTarget "FastCosterUITests" */; + buildPhases = ( + 5ADD9F40298A713400F998F5 /* Sources */, + 5ADD9F41298A713400F998F5 /* Frameworks */, + 5ADD9F42298A713400F998F5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5ADD9F46298A713400F998F5 /* PBXTargetDependency */, + ); + name = FastCosterUITests; + productName = FastCosterUITests; + productReference = 5ADD9F44298A713400F998F5 /* FastCosterUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5ADD9F21298A713300F998F5 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1420; + LastUpgradeCheck = 1420; + TargetAttributes = { + 5ADD9F28298A713300F998F5 = { + CreatedOnToolsVersion = 14.2; + }; + 5ADD9F39298A713400F998F5 = { + CreatedOnToolsVersion = 14.2; + TestTargetID = 5ADD9F28298A713300F998F5; + }; + 5ADD9F43298A713400F998F5 = { + CreatedOnToolsVersion = 14.2; + TestTargetID = 5ADD9F28298A713300F998F5; + }; + }; + }; + buildConfigurationList = 5ADD9F24298A713300F998F5 /* Build configuration list for PBXProject "FastCoster" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5ADD9F20298A713300F998F5; + productRefGroup = 5ADD9F2A298A713300F998F5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5ADD9F28298A713300F998F5 /* FastCoster */, + 5ADD9F39298A713400F998F5 /* FastCosterTests */, + 5ADD9F43298A713400F998F5 /* FastCosterUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5ADD9F27298A713300F998F5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5ADD9F35298A713400F998F5 /* Preview Assets.xcassets in Resources */, + 5ADD9F31298A713400F998F5 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5ADD9F38298A713400F998F5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5ADD9F42298A713400F998F5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 5A450757298D010E00E3D402 /* Create Rust Lib */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Create Rust Lib"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/libcoster_rs.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "source ${PROJECT_DIR}/CosterRs/create-lib.sh\n"; + }; + 5A450758298D014E00E3D402 /* Remove Rust Lib */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Remove Rust Lib"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "source ${PROJECT_DIR}/CosterRs/remove-lib.sh\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5ADD9F25298A713300F998F5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5ADD9F2F298A713300F998F5 /* ContentView.swift in Sources */, + 5ADD9F2D298A713300F998F5 /* FastCosterApp.swift in Sources */, + 5A450751298CE6D500E3D402 /* CsvDocument.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5ADD9F36298A713400F998F5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5ADD9F3F298A713400F998F5 /* FastCosterTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5ADD9F40298A713400F998F5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5ADD9F4B298A713400F998F5 /* FastCosterUITestsLaunchTests.swift in Sources */, + 5ADD9F49298A713400F998F5 /* FastCosterUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 5ADD9F3C298A713400F998F5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5ADD9F28298A713300F998F5 /* FastCoster */; + targetProxy = 5ADD9F3B298A713400F998F5 /* PBXContainerItemProxy */; + }; + 5ADD9F46298A713400F998F5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5ADD9F28298A713300F998F5 /* FastCoster */; + targetProxy = 5ADD9F45298A713400F998F5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 5ADD9F4C298A713400F998F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 5ADD9F4D298A713400F998F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 5ADD9F4F298A713400F998F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = FastCoster/FastCoster.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"FastCoster/Preview Content\""; + DEVELOPMENT_TEAM = TM3BN8YBD9; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + LIBRARY_SEARCH_PATHS = "${DERIVED_FILES_DIR}"; + "LIBRARY_SEARCH_PATHS[arch=*]" = "${DERIVED_FILES_DIR}"; + MACOSX_DEPLOYMENT_TARGET = 13.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Vato.FastCoster; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "${PROJECT_DIR}/CosterRs/coster-bridge.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 5ADD9F50298A713400F998F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = FastCoster/FastCoster.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"FastCoster/Preview Content\""; + DEVELOPMENT_TEAM = TM3BN8YBD9; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + "LIBRARY_SEARCH_PATHS[arch=*]" = "${DERIVED_FILES_DIR}"; + MACOSX_DEPLOYMENT_TARGET = 13.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Vato.FastCoster; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "${PROJECT_DIR}/CosterRs/coster-bridge.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 5ADD9F52298A713400F998F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = TM3BN8YBD9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + MACOSX_DEPLOYMENT_TARGET = 13.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Vato.FastCosterTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FastCoster.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FastCoster"; + }; + name = Debug; + }; + 5ADD9F53298A713400F998F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = TM3BN8YBD9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + MACOSX_DEPLOYMENT_TARGET = 13.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Vato.FastCosterTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FastCoster.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FastCoster"; + }; + name = Release; + }; + 5ADD9F55298A713400F998F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = TM3BN8YBD9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + MACOSX_DEPLOYMENT_TARGET = 13.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Vato.FastCosterUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FastCoster; + }; + name = Debug; + }; + 5ADD9F56298A713400F998F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = TM3BN8YBD9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + MACOSX_DEPLOYMENT_TARGET = 13.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.Vato.FastCosterUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FastCoster; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5ADD9F24298A713300F998F5 /* Build configuration list for PBXProject "FastCoster" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5ADD9F4C298A713400F998F5 /* Debug */, + 5ADD9F4D298A713400F998F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5ADD9F4E298A713400F998F5 /* Build configuration list for PBXNativeTarget "FastCoster" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5ADD9F4F298A713400F998F5 /* Debug */, + 5ADD9F50298A713400F998F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5ADD9F51298A713400F998F5 /* Build configuration list for PBXNativeTarget "FastCosterTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5ADD9F52298A713400F998F5 /* Debug */, + 5ADD9F53298A713400F998F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5ADD9F54298A713400F998F5 /* Build configuration list for PBXNativeTarget "FastCosterUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5ADD9F55298A713400F998F5 /* Debug */, + 5ADD9F56298A713400F998F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5ADD9F21298A713300F998F5 /* Project object */; +} diff --git a/FastCoster/FastCoster.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/FastCoster/FastCoster.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/FastCoster/FastCoster.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/FastCoster/FastCoster.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/FastCoster/FastCoster.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/FastCoster/FastCoster.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/FastCoster/FastCoster/Assets.xcassets/AccentColor.colorset/Contents.json b/FastCoster/FastCoster/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/FastCoster/FastCoster/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FastCoster/FastCoster/Assets.xcassets/AppIcon.appiconset/Contents.json b/FastCoster/FastCoster/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..532cd72 --- /dev/null +++ b/FastCoster/FastCoster/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FastCoster/FastCoster/Assets.xcassets/Contents.json b/FastCoster/FastCoster/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/FastCoster/FastCoster/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FastCoster/FastCoster/ContentView.swift b/FastCoster/FastCoster/ContentView.swift new file mode 100644 index 0000000..c8d9f11 --- /dev/null +++ b/FastCoster/FastCoster/ContentView.swift @@ -0,0 +1,100 @@ +// +// ContentView.swift +// FastCoster +// +// Created by Michael Pivato on 1/2/2023. +// + +import SwiftUI + +struct ContentView: View { + @State private var showRulesPicker = false + @State private var showLinesPicker = false + @State private var showExportPicker = false + @State private var document: CsvDocument? + @State private var lines: String? + @State private var rules: String? + var body: some View { + VStack { + Text ("Fast Coster").font(.largeTitle) + Spacer() + Button { + showRulesPicker.toggle() + } label: { + Text("Select Rules File") + }.fileImporter(isPresented: $showRulesPicker, allowedContentTypes: [.commaSeparatedText]) { result in + + switch result { + case .success(let fileUrl): + rules = readIntoString(selectedFile: fileUrl) + case .failure(let error): + print(error) + } + }.padding() + Button { + showLinesPicker.toggle() + } label: { + Text("Select Lines File") + }.fileImporter(isPresented: $showLinesPicker, allowedContentTypes: [.commaSeparatedText]) { result in + switch result { + case .success(let fileUrl): + lines = readIntoString(selectedFile: fileUrl) + case .failure(let error): + print(error) + } + }.padding() + Button { + move_money() + } label: { + Text("Move money") + }.padding() + Spacer() + } + .padding() + .fileExporter(isPresented: $showExportPicker, document: document, contentType: .commaSeparatedText) { result in + + if case .success = result { + + }else { + + } + + } + } + + func move_money() { + DispatchQueue.global(qos: .userInitiated).async { + // Run move money + let result = move_money_from_text(rules, lines, true) + + DispatchQueue.main.async { + document = CsvDocument(data: String(cString: result!)) + move_money_from_text_free(result) + showExportPicker = true; + } + } + } + + func readIntoString(selectedFile: URL) -> String { + // https://stackoverflow.com/questions/64118577/getting-the-file-xxx-couldnt-be-opened-because-you-dont-have-permission-to-v + do { + if selectedFile.startAccessingSecurityScopedResource() { + let fileContent = try String(contentsOf: selectedFile) + defer { selectedFile.stopAccessingSecurityScopedResource() } + return fileContent + } + }catch let error { + // TODO: Do something about this + print(error) + } + return "" + } + + +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/FastCoster/FastCoster/CsvDocument.swift b/FastCoster/FastCoster/CsvDocument.swift new file mode 100644 index 0000000..7acebf4 --- /dev/null +++ b/FastCoster/FastCoster/CsvDocument.swift @@ -0,0 +1,33 @@ +// +// CsvDocument.swift +// FastCoster +// +// Created by Michael Pivato on 3/2/2023. +// + +import Foundation +import SwiftUI +import UniformTypeIdentifiers + +struct CsvDocument: FileDocument { + var output: String + + static var readableContentTypes: [UTType] {[.commaSeparatedText]} + + init(configuration: ReadConfiguration) throws { + guard let data = configuration.file.regularFileContents, let string = String(data: data, encoding: .utf8) else { + throw CocoaError(.fileReadCorruptFile) + } + output = string + } + + init(data: String) { + output = data + } + + func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { + return FileWrapper(regularFileWithContents: output.data(using: .utf8)!) + } + + +} diff --git a/FastCoster/FastCoster/FastCoster.entitlements b/FastCoster/FastCoster/FastCoster.entitlements new file mode 100644 index 0000000..19afff1 --- /dev/null +++ b/FastCoster/FastCoster/FastCoster.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/FastCoster/FastCoster/FastCosterApp.swift b/FastCoster/FastCoster/FastCosterApp.swift new file mode 100644 index 0000000..a72ceb0 --- /dev/null +++ b/FastCoster/FastCoster/FastCosterApp.swift @@ -0,0 +1,17 @@ +// +// FastCosterApp.swift +// FastCoster +// +// Created by Michael Pivato on 1/2/2023. +// + +import SwiftUI + +@main +struct FastCosterApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/FastCoster/FastCoster/Preview Content/Preview Assets.xcassets/Contents.json b/FastCoster/FastCoster/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/FastCoster/FastCoster/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FastCoster/FastCosterTests/FastCosterTests.swift b/FastCoster/FastCosterTests/FastCosterTests.swift new file mode 100644 index 0000000..f690d90 --- /dev/null +++ b/FastCoster/FastCosterTests/FastCosterTests.swift @@ -0,0 +1,35 @@ +// +// FastCosterTests.swift +// FastCosterTests +// +// Created by Michael Pivato on 1/2/2023. +// + +import XCTest + +final class FastCosterTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/FastCoster/FastCosterUITests/FastCosterUITests.swift b/FastCoster/FastCosterUITests/FastCosterUITests.swift new file mode 100644 index 0000000..06d4583 --- /dev/null +++ b/FastCoster/FastCosterUITests/FastCosterUITests.swift @@ -0,0 +1,41 @@ +// +// FastCosterUITests.swift +// FastCosterUITests +// +// Created by Michael Pivato on 1/2/2023. +// + +import XCTest + +final class FastCosterUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/FastCoster/FastCosterUITests/FastCosterUITestsLaunchTests.swift b/FastCoster/FastCosterUITests/FastCosterUITestsLaunchTests.swift new file mode 100644 index 0000000..ebdd1e1 --- /dev/null +++ b/FastCoster/FastCosterUITests/FastCosterUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// FastCosterUITestsLaunchTests.swift +// FastCosterUITests +// +// Created by Michael Pivato on 1/2/2023. +// + +import XCTest + +final class FastCosterUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index ee7c5bb..3c1e98f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,8 @@ mod move_money; +use std::ffi::c_char; +use std::ffi::CStr; +use std::ffi::CString; + pub use self::move_money::*; mod smush_rules; @@ -6,3 +10,47 @@ pub use self::smush_rules::*; mod overhead_allocation; pub use self::overhead_allocation::*; + +#[no_mangle] +pub extern "C" fn move_money_from_text( + rules: *const c_char, + lines: *const c_char, + use_numeric_accounts: bool, +) -> *mut c_char { + let mut output_writer = csv::Writer::from_writer(vec![]); + let safe_rules = unsafe { + assert!(!rules.is_null()); + CStr::from_ptr(rules) + }; + let safe_lines = unsafe { + assert!(!lines.is_null()); + CStr::from_ptr(lines) + }; + move_money( + csv::Reader::from_reader(safe_rules.to_bytes()), + csv::Reader::from_reader(safe_lines.to_bytes()), + &mut output_writer, + use_numeric_accounts, + ); + // TODO: Replace all these unwraps with something more elegant + let inner = output_writer.into_inner().unwrap(); + CString::new(String::from_utf8(inner).unwrap()) + .unwrap() + .into_raw() + + // Also some resources I looked at, in case things aren't going right: + // https://notes.huy.rocks/en/string-ffi-rust.html + // http://jakegoulding.com/rust-ffi-omnibus/string_return/ + // https://rust-unofficial.github.io/patterns/idioms/ffi/passing-strings.html + // This looks like exactly what I'm doing too: https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.htmlcar +} + +#[no_mangle] +pub extern "C" fn move_money_from_text_free(s: *mut c_char) { + unsafe { + if s.is_null() { + return; + } + CString::from_raw(s) + }; +} diff --git a/src/main.rs b/src/main.rs index 36434b8..2db926c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, error::Error, io::Write, path::PathBuf}; use clap::{Parser, Subcommand}; -use coster_rs::{CsvCost, Unit}; +use coster_rs::CsvCost; use serde::Deserialize; #[derive(Parser)] @@ -79,7 +79,7 @@ fn move_money( coster_rs::move_money( csv::Reader::from_path(rules)?, csv::Reader::from_path(lines)?, - csv::Writer::from_path(output.unwrap_or(PathBuf::from("output.csv")))?, + &mut csv::Writer::from_path(output.unwrap_or(PathBuf::from("output.csv")))?, use_numeric_accounts, ) } diff --git a/src/move_money.rs b/src/move_money.rs index 0aa7b34..8b2cf71 100644 --- a/src/move_money.rs +++ b/src/move_money.rs @@ -92,7 +92,7 @@ pub struct CsvCost { pub fn move_money( rules_reader: csv::Reader, lines_reader: csv::Reader, - output: csv::Writer, + output: &mut csv::Writer, use_numeric_accounts: bool, ) -> anyhow::Result<()> where @@ -326,7 +326,7 @@ mod tests { super::move_money( csv::Reader::from_path("reclassrule.csv").unwrap(), csv::Reader::from_path("line.csv").unwrap(), - csv::Writer::from_path("output.csv").unwrap(), + &mut csv::Writer::from_path("output.csv").unwrap(), true, ); } diff --git a/src/overhead_allocation.rs b/src/overhead_allocation.rs index ec5b85d..14765db 100644 --- a/src/overhead_allocation.rs +++ b/src/overhead_allocation.rs @@ -1,7 +1,8 @@ -use std::collections::HashMap; +use std::{collections::HashMap, io::Read, path::Path}; use itertools::Itertools; use nalgebra::{DMatrix, Dynamic, LU}; +use serde::Deserialize; #[derive(Debug, PartialEq, Eq)] pub enum DepartmentType { @@ -9,6 +10,36 @@ pub enum DepartmentType { Overhead, } +#[derive(Deserialize)] +pub struct CsvAllocationStatistic { + #[serde(rename = "Name")] + name: String, + #[serde(rename = "Description")] + description: Option, + #[serde(rename = "AccountType")] + account_type: String, + #[serde(rename = "AccountRanges")] + account_ranges: String, +} + +#[derive(Deserialize)] +pub struct CsvAccount { + #[serde(rename = "Code")] + code: String, + #[serde(rename = "Description")] + description: Option, + #[serde(rename = "Type")] + account_type: String, + #[serde(rename = "CostOutput")] + cost_output: Option, + #[serde(rename = "PercentFixed")] + percent_fixed: f64, +} + +type CsvCostCentre = HashMap; + +type CsvArea = HashMap; + // Note: remember these are overhead departments only when calculating the lu decomposition or pseudoinverse, and for each department, // you either need -1 or rest negative for a row to subtract the initial amounts so we end up effectively 0 (simultaneous equations end // up with negative there so yes this is expected) @@ -65,10 +96,32 @@ fn get_rules_indexes( .collect() } +pub fn reciprocal_allocation( + lines: csv::Reader, + accounts: csv::Reader, + allocation_statistics: csv::Reader, + areas: csv::Reader, + cost_centres: csv::Reader, + output: csv::Writer, +) where + Lines: Read, + Account: Read, + AllocationStatistic: Read, + Area: Read, + CostCentre: Read, + Output: std::io::Write, +{ + // Build out the the list of allocation rules from areas/allocation statistics (similar to ppm building 'cost drivers') + + // do reciprocal allocation (only for variable portion of accounts), for each account + + // Copy across fixed stuff (if necessary, not sure it is) +} + // Perform the reciprocal allocation (matrix) method to allocate servicing departments (indirect) costs // to functional departments. Basically just a matrix solve, uses regression (moore-penrose pseudoinverse) when // matrix is singular -pub fn reciprocal_allocation( +fn reciprocal_allocation_impl( allocations: Vec, account_costs: Vec, // TODO: Throw an appropriate error