diff --git a/packages/console/lib/common.dart b/packages/console/lib/common.dart new file mode 100644 index 0000000..dbfe4c1 --- /dev/null +++ b/packages/console/lib/common.dart @@ -0,0 +1,21 @@ +/* + * This file is part of the Protevus Platform. + * + * (C) Protevus + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/// This library file exports common utility functions and controllers used in the Protevus Console package. +/// +/// It includes: +/// - Functionality for determining paths, exported from 'determine_paths.dart' +/// - A unit test controller, exported from 'unit_test_controller.dart' +/// +/// These exports allow other parts of the application to easily access and use +/// these common utilities without needing to import them individually. +library; + +export 'package:protevus_console/src/common/determine_paths.dart'; +export 'package:protevus_console/src/common/unit_test_controller.dart'; diff --git a/packages/console/lib/src/.gitkeep b/packages/console/lib/src/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/packages/console/lib/src/common/determine_paths.dart b/packages/console/lib/src/common/determine_paths.dart new file mode 100644 index 0000000..f296798 --- /dev/null +++ b/packages/console/lib/src/common/determine_paths.dart @@ -0,0 +1,144 @@ +/* + * This file is part of the Protevus Platform. + * + * (C) Protevus + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import 'dart:io'; +import 'package:path/path.dart' as p; +import 'package:path/path.dart'; + +/// Determines the paths for source and backup directories based on the given parameters. +/// +/// [path] is the relative or absolute path to the +/// file that we are going to backup. If [path] is +/// relative then it is relative to [workingDirectory]. +/// [backupDir] is the temporary directory that we +/// are going to backup [path] to. +/// +/// We use the following directory structure for the backup +/// relative/ +/// absolute/ +/// +/// On Windows to accomodate drive letters we need a slightly +/// different directory structure +/// relative/ +/// absolute// +/// +/// Where 'X' is the drive letter that [path] is located on. +/// +Paths determinePaths({ + required String path, + required String workingDirectory, + required String backupDir, +}) { + late final String sourcePath; + late final String backupPath; + + /// we use two different directories for relative and absolute + /// paths otherwise we can't differentiate when it comes time + /// to restore. + if (isRelative(path)) { + backupPath = normalize(absolute(join(backupDir, 'relative', path))); + sourcePath = join(workingDirectory, path); + } else { + sourcePath = normalize(absolute(path)); + final translatedPath = + translateAbsolutePath(path, workingDirectory: workingDirectory); + backupPath = join(backupDir, 'absolute', _stripRootPrefix(translatedPath)); + } + + return Paths(sourcePath, backupPath); +} + +/// Represents a pair of paths for source and backup files. +/// +/// This class is used to store and manage the paths for a source file +/// and its corresponding backup file. +/// +/// [sourcePath] is the path to the original source file. +/// [backupPath] is the path where the backup of the source file will be stored. +class Paths { + Paths(this.sourcePath, this.backupPath); + + String sourcePath; + String backupPath; +} + +/// Removes the root prefix (/ or \) from an absolute path +/// If there is no root prefix the original [absolutePath] +/// is returned untouched. +/// +/// If the [absolutePath] only contains the root prefix +/// then a blank string is returned +/// +/// /hellow -> hellow +/// hellow -> hellow +/// / -> +/// +String? _stripRootPrefix(String absolutePath) { + if (absolutePath.startsWith(r'\') || absolutePath.startsWith('/')) { + if (absolutePath.length > 1) { + return absolutePath.substring(1); + } else { + // the path only contained the root prefix and nothing else. + return ''; + } + } + return absolutePath; +} + +/// Translates an absolute path to a standardized format, primarily for Windows systems. +/// +/// C:/abc -> /CDrive/abc +/// C:\abc -> /CDrive\abc +/// \\\abc -> \abc +/// \\abc -> abc +/// +/// The [context] is only used for unit testing so +/// we can fake the platform separator. +String translateAbsolutePath( + String absolutePath, { + String? workingDirectory, + p.Context? context, +}) { + final windowsStyle = context != null && context.style == Style.windows; + if (!windowsStyle && !Platform.isWindows) { + return absolutePath; + } + + context ??= p.context; + + // ignore: parameter_assignments + workingDirectory ??= Directory.current.path; + + final parts = context.split(absolutePath); + if (parts[0].contains(':')) { + final index = parts[0].indexOf(':'); + + final drive = parts[0][index - 1].toUpperCase(); + return context.joinAll(['\\${drive}Drive', ...parts.sublist(1)]); + } + + if (parts[0].startsWith(r'\\')) { + final uncparts = parts[0].split(r'\\'); + return context.joinAll([r'\UNC', ...uncparts.sublist(1)]); + } + + if (absolutePath.startsWith(r'\') || absolutePath.startsWith('/')) { + String drive; + if (workingDirectory.contains(':')) { + drive = workingDirectory[0].toUpperCase(); + } else { + drive = Directory.current.path[0].toUpperCase(); + } + return context.joinAll(['\\${drive}Drive', ...parts.sublist(1)]); + } + + /// probably not an absolute path + /// so just pass back what we were handed. + return absolutePath; +} diff --git a/packages/console/lib/src/common/unit_test_controller.dart b/packages/console/lib/src/common/unit_test_controller.dart new file mode 100644 index 0000000..2e6604b --- /dev/null +++ b/packages/console/lib/src/common/unit_test_controller.dart @@ -0,0 +1,56 @@ +/* + * This file is part of the Protevus Platform. + * + * (C) Protevus + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import 'package:scope/scope.dart'; + +/// A utility class for managing unit test behavior in DCli. +/// +/// This class provides static members to control and detect when code is running +/// within a unit test environment. It uses the `scope` package to manage a boolean +/// flag indicating whether the current execution context is a unit test. +class UnitTestController { + /// A ScopeKey used to indicate whether the current execution is within a unit test. + /// + /// This key is injected when running a unit test, allowing DCli code to be + /// 'unit test' aware and modify its behavior to be unit test friendly. + /// The default value is false, indicating that by default, the code is not + /// running in a unit test environment. + /// + /// Usage: + /// - When set to true, it signals that the code is running within a unit test. + /// - DCli functions can check this key to adjust their behavior accordingly. + static final unitTestingKey = + ScopeKey.withDefault(false, 'Running in a unit test'); + + /// Executes the provided action within a unit test context. + /// + /// This method creates a new [Scope] where the [unitTestingKey] is set to true, + /// indicating that the code is running within a unit test environment. It then + /// executes the provided [action] within this scope. + /// + /// Certain DCli functions modify their behavior when run within a unit test. + /// They rely on this scope to determine if they are in a unit test environment. + /// + /// Usage: + /// ```dart + /// await UnitTestController.withUnitTest(() { + /// // Your unit test code here + /// }); + /// ``` + /// + /// Parameters: + /// - action: A void function that contains the code to be executed within the unit test context. + /// + /// Returns: + /// A [Future] that completes when the action has finished executing. + static Future withUnitTest(void Function() action) async { + final scope = Scope()..value(unitTestingKey, true); + await scope.run(() async => action()); + } +} diff --git a/packages/console/pubspec.yaml b/packages/console/pubspec.yaml index 97b662f..19a4db6 100644 --- a/packages/console/pubspec.yaml +++ b/packages/console/pubspec.yaml @@ -10,7 +10,8 @@ environment: # Add regular dependencies here. dependencies: - # path: ^1.8.0 + path: ^1.9.0 + scope: ^4.1.0 dev_dependencies: lints: ^3.0.0