Ver código fonte

refactor: implement program args string parse in terms of string-utils

Sam Jaffe 3 anos atrás
pai
commit
b8eb3f2714

+ 3 - 0
.gitmodules

@@ -1,3 +1,6 @@
 [submodule "external/scoped_buffer_capture"]
 	path = external/scoped_buffer_capture
 	url = ssh://git@gogs.sjaffe.name:3000/sjjaffe/cpp-scoped-buffer-capture.git
+[submodule "external/string-utils"]
+	path = external/string-utils
+	url = ssh://git@gogs.sjaffe.name:3000/sjjaffe/cpp-string-utils.git

+ 1 - 0
external/string-utils

@@ -0,0 +1 @@
+Subproject commit 32b877b3ab7aedd6178ea3a8a206555cba9b6c9f

+ 21 - 68
include/program_args/utilities.h

@@ -4,32 +4,12 @@
 #include <type_traits>
 #include <vector>
 
-namespace program::traits {
-template <typename T, typename = void>
-struct is_repeatable : std::false_type {};
+#include <string_utils/cast.h>
 
-template <typename F>
-struct is_repeatable<F, std::enable_if_t<!std::is_void_v<
-                            std::result_of_t<F(std::vector<std::string>)>>>>
-    : std::true_type {};
-
-template <typename T>
-constexpr bool const is_repeatable_v = is_repeatable<T>::value;
-
-template <typename T, typename = void> struct is_container : std::false_type {};
-template <> struct is_container<std::string> : std::false_type {};
-template <typename T>
-struct is_container<T, std::void_t<typename T::value_type>> : std::true_type {};
-
-template <typename T>
-constexpr bool const is_container_v = is_container<T>::value;
-
-template <typename T, typename = void> struct is_associative : std::false_type {};
-template <typename T>
-struct is_associative<T, std::void_t<typename T::mapped_type>> : std::true_type {};
-
-template <typename T>
-constexpr bool const is_associative_v = is_associative<T>::value;
+namespace program::detail {
+template <typename, typename, typename = void> struct has_cast : std::false_type {};
+template <typename T, typename S>
+struct has_cast<T, S, std::void_t<decltype(string_utils::cast(S(), std::declval<T&>()))>> : std::true_type {};
 }
 
 namespace program {
@@ -53,33 +33,12 @@ template <typename T, typename = void> struct conversion_helper;
  * \return An object of the given type
  */
 template <typename T>
-T convert(std::string const & name, std::string const & data) try {
-  return conversion_helper<T>{}(data);
-} catch (std::exception const & ex) {
-  throw ArgumentStructureError(ex.what(), name);
-}
-
-template <typename T, typename V = typename T::mapped_type>
-T convert_associative(std::string const & name, std::vector<std::string> const &args) {
-  T rval;
-  for (std::string const &arg : args) {
-    size_t pos = arg.find('=');
-    if (pos == std::string::npos) {
-      throw ArgumentStructureError("expected argument of the form key=value, got " + arg,
-                                   name);
-    }
-    rval.emplace(arg.substr(0, pos), convert<V>(name, arg.substr(pos + 1)));
-  }
-  return rval;
-}
-
-template <typename T, typename V = typename T::value_type>
-T convert_container(std::string const & name, std::vector<std::string> const &args) {
-  T rval;
-  for (std::string const &arg : args) {
-    rval.insert(rval.end(), convert<V>(name, arg));
+T convert(std::string const & name, std::string const & data) {
+  if (auto [rval, success] = string_utils::cast<T>(data); success) {
+    return rval;
+  } else {
+    throw ArgumentStructureError("unable to parse", name);
   }
-  return rval;
 }
 
 /**
@@ -94,27 +53,21 @@ T convert_container(std::string const & name, std::vector<std::string> const &ar
  */
 template <typename T>
 T convert(std::string const & name, std::vector<std::string> const & data) {
-  if constexpr (traits::is_associative_v<T>) {
-    return convert_associative<T>(name, data);
-  } else if constexpr (traits::is_container_v<T> && !std::is_constructible_v<T, std::string>) {
-    return convert_container<T>(name, data);
-  } else if (data.size() == 1) {
-    return convert<T>(name, data[0]);
+  if constexpr (detail::has_cast<T, std::string>{}) {
+    if (data.size() != 1) {
+      throw ArgumentStructureError("repeated option not allowed", name);
+    } else {
+      return convert<T>(name, data.front());
+    }
   } else {
-    throw ArgumentStructureError("Repeated option not allowed", name);
+    if (auto [rval, success] = string_utils::cast<T>(data); success) {
+      return rval;
+    } else {
+      throw ArgumentStructureError("unable to parse", name);
+    }
   }
 }
 
-template <typename T>
-struct conversion_helper<
-    T, std::enable_if_t<std::is_convertible_v<std::string, T>>> {
-  T operator()(std::string const & str) const { return T(str); }
-};
-
-template <> struct conversion_helper<int> {
-  int operator()(std::string const & str) const { return std::stoi(str); }
-};
-
 using std::to_string;
 template <typename T> std::string to_string(T const &) { return "?"; }
 inline std::string to_string(char const * str) { return str; }

+ 62 - 4
program_args.xcodeproj/project.pbxproj

@@ -30,6 +30,27 @@
 			remoteGlobalIDString = CDE4F78925CF309E009E4EC1;
 			remoteInfo = program_args;
 		};
+		CDCB1D8328567635001C406D /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDCB1D7D28567635001C406D /* string-utils.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = CD266862252FF4B600B3E667;
+			remoteInfo = "string-utils";
+		};
+		CDCB1D8528567635001C406D /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDCB1D7D28567635001C406D /* string-utils.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = CD266886252FFAAE00B3E667;
+			remoteInfo = "string_utils-test";
+		};
+		CDCB1D892856793F001C406D /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDCB1D7D28567635001C406D /* string-utils.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = CD266861252FF4B600B3E667;
+			remoteInfo = "string-utils";
+		};
 		CDE4F79A25CF316A009E4EC1 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = CDE4F79325CF316A009E4EC1 /* GoogleMock.xcodeproj */;
@@ -73,6 +94,7 @@
 		CD8C5A9625D058470004A6D9 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
 		CD8C5A9F25D06D0B0004A6D9 /* flag_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = flag_test.cpp; sourceTree = "<group>"; };
 		CD8C5AA325D072F50004A6D9 /* argument_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = argument_test.cpp; sourceTree = "<group>"; };
+		CDCB1D7D28567635001C406D /* string-utils.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "string-utils.xcodeproj"; path = "external/string-utils/string-utils.xcodeproj"; sourceTree = "<group>"; };
 		CDD334A025D200AB008540EE /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
 		CDE4AAB827B9EBF000543450 /* action_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = action_test.cpp; sourceTree = "<group>"; };
 		CDE4F78A25CF309E009E4EC1 /* libprogram_args.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprogram_args.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -141,10 +163,20 @@
 			path = program_args;
 			sourceTree = "<group>";
 		};
+		CDCB1D7E28567635001C406D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CDCB1D8428567635001C406D /* libstring-utils.a */,
+				CDCB1D8628567635001C406D /* string_utils-test.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		CDE4F78125CF309E009E4EC1 = {
 			isa = PBXGroup;
 			children = (
 				CD2F0C5325DC9AE000CB394A /* scoped_buffer_capture.xcodeproj */,
+				CDCB1D7D28567635001C406D /* string-utils.xcodeproj */,
 				CDD334A025D200AB008540EE /* README.md */,
 				CDE4F79325CF316A009E4EC1 /* GoogleMock.xcodeproj */,
 				CD8C5A8E25D057C00004A6D9 /* include */,
@@ -213,6 +245,7 @@
 			buildRules = (
 			);
 			dependencies = (
+				CDCB1D8A2856793F001C406D /* PBXTargetDependency */,
 				CD8C5A7C25D0577E0004A6D9 /* PBXTargetDependency */,
 			);
 			name = "program_args-test";
@@ -273,6 +306,10 @@
 					ProductGroup = CD2F0C5425DC9AE000CB394A /* Products */;
 					ProjectRef = CD2F0C5325DC9AE000CB394A /* scoped_buffer_capture.xcodeproj */;
 				},
+				{
+					ProductGroup = CDCB1D7E28567635001C406D /* Products */;
+					ProjectRef = CDCB1D7D28567635001C406D /* string-utils.xcodeproj */;
+				},
 			);
 			projectRoot = "";
 			targets = (
@@ -290,6 +327,20 @@
 			remoteRef = CD2F0C5B25DC9AE000CB394A /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		CDCB1D8428567635001C406D /* libstring-utils.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = "libstring-utils.a";
+			remoteRef = CDCB1D8328567635001C406D /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CDCB1D8628567635001C406D /* string_utils-test.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "string_utils-test.xctest";
+			remoteRef = CDCB1D8528567635001C406D /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		CDE4F79B25CF316A009E4EC1 /* GoogleMock.framework */ = {
 			isa = PBXReferenceProxy;
 			fileType = wrapper.framework;
@@ -358,6 +409,11 @@
 			target = CDE4F78925CF309E009E4EC1 /* program_args */;
 			targetProxy = CD8C5A7B25D0577E0004A6D9 /* PBXContainerItemProxy */;
 		};
+		CDCB1D8A2856793F001C406D /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = "string-utils";
+			targetProxy = CDCB1D892856793F001C406D /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -449,10 +505,11 @@
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = (
-					include/,
+				SYSTEM_HEADER_SEARCH_PATHS = (
+					"external/string-utils/include/",
 					external/scoped_buffer_capture/include/,
 				);
+				USER_HEADER_SEARCH_PATHS = include/;
 			};
 			name = Debug;
 		};
@@ -505,10 +562,11 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = (
-					include/,
+				SYSTEM_HEADER_SEARCH_PATHS = (
+					"external/string-utils/include/",
 					external/scoped_buffer_capture/include/,
 				);
+				USER_HEADER_SEARCH_PATHS = include/;
 			};
 			name = Release;
 		};

+ 0 - 78
program_args.xcodeproj/xcshareddata/xcschemes/program_args.xcscheme

@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1240"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "CDE4F78925CF309E009E4EC1"
-               BuildableName = "libprogram_args.a"
-               BlueprintName = "program_args"
-               ReferencedContainer = "container:program_args.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      codeCoverageEnabled = "YES">
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "CD8C5A7425D0577E0004A6D9"
-               BuildableName = "program_args-test.xctest"
-               BlueprintName = "program_args-test"
-               ReferencedContainer = "container:program_args.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "CDE4F78925CF309E009E4EC1"
-            BuildableName = "libprogram_args.a"
-            BlueprintName = "program_args"
-            ReferencedContainer = "container:program_args.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>