commit 0e42917578a7307baf2987b841874e526a6c2ad7 Author: youmetme <321640253@qq.com> Date: Mon Jul 14 16:12:38 2025 +0800 feat: v0.2.0 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0e9cdda --- /dev/null +++ b/.clang-format @@ -0,0 +1,246 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..24a8e87 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.png filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b5dfc6d --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# ---> C +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# ---> CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + + +build +.cache +.vscode \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b32a7a2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.28) + +project(CArgParse) + +if(MSVC) + add_compile_options(/utf-8) +endif(MSVC) + + +option(SHARED_BUILD "Build shared library" OFF) +option(TEST "Build tests" ON) +option(EXAMPLE "Build examples" ON) + +set(CMAKE_C_STANDARD 11) + +enable_testing() + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +include_directories(include) + +aux_source_directory(src SRC) + +if(SHARED_BUILD) + message(STATUS "Building shared library") + add_library(${PROJECT_NAME} SHARED ${SRC}) +else() + message(STATUS "Building static library") + add_library(${PROJECT_NAME} ${SRC}) +endif(SHARED_BUILD) + +if(TEST) + add_subdirectory(tests) +endif(TEST) + +if(EXAMPLE) + add_subdirectory(examples) +endif(EXAMPLE) + + + +# install +install(TARGETS ${PROJECT_NAME} DESTINATION lib) +install(FILES include/CArgParse.h DESTINATION include) diff --git a/README.md b/README.md new file mode 100644 index 0000000..09f2dec --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# CArgParse + +C语言易用的命令参数分析 + + +# 特性 +- 自动生成帮助信息 +- 默认值支持 +- 子命令支持 + + +# 安装 + +## cmake安装 +```bash +cmake -S . -B build +cmake --build build +cmake --install build +``` + + + +## conan安装 +将库构建为`conan`包 +```bash +conan create . +``` + + + +## 示例程序 +构建简单的命令行程序 +```c +#include "ArgParse.h" +#include +#include +#include + +ArgParse *Init() { + ArgParse *ap = argParseInit("简单的命令行工具示例"); + + // 添加第一个命令 + Command *cmd = argParseAddCommand( + ap, "list", "列出文件列表", NULL, NULL, NULL, SINGLEVALUE); + + // 添加第一个命令的参数 + argParseAddArg(cmd, + "-a", + "--all", + "列出所有文件包括隐藏文件", + NULL, + NULL, + false, + NOVALUE); + + return ap; +} + +int main(int argc, char *argv[]) { + ArgParse *ap = Init(); + argParseParse(ap, argc, argv); + + char *dir = argParseGetCurCommandValue(ap); + if (dir != NULL) { + printf("列出目录: %s 的文件列表\n", dir); + } + + if (argParseCheckCurArgTriggered(ap, "-a")) { + printf("触发了 -a 参数,列出所有文件\n"); + } + + argParseFree(ap); + return 0; +} +``` + +```bash +example -h +``` +![帮助信息](docs/images/help.png) \ No newline at end of file diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..e1fe684 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,112 @@ +from conan import ConanFile +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import copy +import os + + +class loggingRecipe(ConanFile): + name = "cargparse" + version = "0.2.0" + license = "MIT" + author = "321640253@qq.com" + url = "https://gitea.youmetme.wang/youmetme/logging" + description = "C语言命令行参数解析器" + topics = ( + "argparse", + "C", + "simple", + "easy-to-use", + "CArgParse", + "args", + "cargparse", + ) + + settings = "os", "compiler", "build_type", "arch" + options = { + "shared": [True, False], + "fPIC": [True, False], + "test": [True, False], + "example": [True, False], + } + default_options = {"shared": False, "fPIC": True, "test": True, "example": False} + + exports_sources = "include/*", "CMakeLists.txt", "src/*", "tests/*" + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.variables["TEST"] = True if self.options.test else False + tc.variables["SHARED_BUILD"] = True if self.options.shared else False + tc.variables["EXAMPLE"] = True if self.options.example else False + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + if self.options.test: + cmake.test() + + def package(self): + copy( + self, + "LICENSE", + src=self.source_folder, + dst=os.path.join(self.package_folder, "licenses"), + ) + copy( + self, + pattern="*.h", + src=os.path.join(self.source_folder, "include"), + dst=os.path.join(self.package_folder, "include"), + ) + copy( + self, + pattern="*.a", + src=self.build_folder, + dst=os.path.join(self.package_folder, "lib"), + keep_path=False, + ) + copy( + self, + pattern="*.so", + src=self.build_folder, + dst=os.path.join(self.package_folder, "lib"), + keep_path=False, + ) + copy( + self, + pattern="*.lib", + src=self.build_folder, + dst=os.path.join(self.package_folder, "lib"), + keep_path=False, + ) + copy( + self, + pattern="*.dll", + src=self.build_folder, + dst=os.path.join(self.package_folder, "bin"), + keep_path=False, + ) + copy( + self, + pattern="*.dylib", + src=self.build_folder, + dst=os.path.join(self.package_folder, "lib"), + keep_path=False, + ) + + def package_info(self): + self.cpp_info.libs = ["cargparse"] diff --git a/docs/images/help.png b/docs/images/help.png new file mode 100644 index 0000000..6018c5b --- /dev/null +++ b/docs/images/help.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f407f84504fb8e52cfaa92baf79f5100a2839643a21ccd8278e2d95b05a46e1 +size 35360 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..c330409 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,5 @@ +project(example) + + +add_executable(example simple.c) +target_link_libraries(example CArgParse) diff --git a/examples/simple.c b/examples/simple.c new file mode 100644 index 0000000..8071733 --- /dev/null +++ b/examples/simple.c @@ -0,0 +1,41 @@ +#include "ArgParse.h" +#include +#include +#include + +ArgParse *Init() { + ArgParse *ap = argParseInit("简单的命令行工具示例"); + + // 添加第一个命令 + Command *cmd = argParseAddCommand( + ap, "list", "列出文件列表", NULL, NULL, NULL, SINGLEVALUE); + + // 添加第一个命令的参数 + argParseAddArg(cmd, + "-a", + "--all", + "列出所有文件包括隐藏文件", + NULL, + NULL, + false, + NOVALUE); + + return ap; +} + +int main(int argc, char *argv[]) { + ArgParse *ap = Init(); + argParseParse(ap, argc, argv); + + char *dir = argParseGetCurCommandValue(ap); + if (dir != NULL) { + printf("列出目录: %s 的文件列表\n", dir); + } + + if (argParseCheckCurArgTriggered(ap, "-a")) { + printf("触发了 -a 参数,列出所有文件\n"); + } + + argParseFree(ap); + return 0; +} \ No newline at end of file diff --git a/include/ArgParse.h b/include/ArgParse.h new file mode 100644 index 0000000..48143ca --- /dev/null +++ b/include/ArgParse.h @@ -0,0 +1,306 @@ +#ifndef CARGPARSE_H +#define CARGPARSE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARG_DEFAULT_HELP_FLAG "--help" + +typedef struct ArgParse ArgParse; // 解析器 + +typedef int (*ArgParseCallback)(ArgParse *argParse, + char **val, + int val_len); // 回调函数 + +typedef enum { + NOVALUE = 0, // 无值 + SINGLEVALUE, // 单值 例如: -i https://www.baidu.com + MULTIVALUE, // 多值 例如: -s a b c 或 -s a -s b -s c等 +} ArgParseValueType; // 值类型 + +typedef struct CommandArgs { + /* 构造属性 */ + char *short_opt; // 短选项名, 例如: -h + char *long_opt; // 长选项 例如: --help + char *default_val; // 默认值 + char *help; // 选项帮助信息 + ArgParseCallback callback; // 回调函数 + bool required; // 是否为必选参数 + ArgParseValueType value_type; // 值类型 + /* 解析所用到的属性*/ + char **val; // 解析到的值 + int val_len; // 解析到的值个数 + bool is_trigged; // 是否被触发 +} CommandArgs; + +typedef struct CommandGroup { + char *name; // 命令组名 + char *help; // 命令组帮助信息 + struct Command **commands; // 命令 + int commands_len; // 命令个数 +} CommandGroup; + +typedef struct Command { + /* 构造属性 */ + char *name; // 命令名 + char *help; // 命令帮助信息 + CommandGroup *group; // 命令组 + char *default_val; // 默认值 + struct CommandArgs **args; // 命令参数 + int args_len; // 命令参数个数 + struct Command **sub_commands; // 子命令 + int sub_commands_len; // 子命令个数 + ArgParseCallback callback; // 回调函数 + ArgParseValueType value_type; // 值类型 + + /* 解析所用到的属性*/ + char **val; // 解析到的值 + int val_len; // 解析到的值个数 + bool is_trigged; // 是否被触发 +} Command; + +typedef struct ArgParse { + /* 构造属性 */ + struct Command **commands; // 命令 + int commands_len; // 命令个数 + struct CommandArgs **global_args; // 全局参数 + int global_args_len; // 全局参数个数 + char *documentation; // 帮助文档 + /* 解析所用到的属性*/ + struct Command *current_command; // 当前解析到的命令 + int argc; // 参数个数 + char **argv; // 参数列表 +} ArgParse; + +/** Start---------------构造API---------------- */ + +/** + * @brief 初始化解析器 + * @return ArgParse* 解析器指针 + */ +ArgParse *argParseInit(char *documentation); + +/** + * @brief 释放解析器 + * @param argParse 解析器指针 + * @return void + */ +void argParseFree(ArgParse *argParse); + +/** + * @brief 自动生成帮助信息 + * @param argParse 解析器指针 + * @return void + */ +void argParseAutoHelp(ArgParse *argParse); + +/** + * @brief 添加命令 + * @param argParse 解析器指针 + * @param name 命令名 + * @param help 命令帮助信息 + * @param group_name 命令组名 + * @return Command* 命令指针 + */ +Command *argParseAddCommand(ArgParse *argParse, + char *name, + char *help, + char *default_val, // 默认值 + ArgParseCallback callback, + CommandGroup *group, + ArgParseValueType value_type); + +/** + * @brief 添加子命令 + * @param Parent 父命令指针 + * @param name 命令名 + * @param help 命令帮助信息 + * @param callback 回调函数 + * @param value_type 值类型 + * @return Command* 子命令指针 + */ +Command *argParseAddSubCommand(Command *Parent, + char *name, + char *help, + char *default_val, + ArgParseCallback callback, + CommandGroup *group, + ArgParseValueType value_type); + +/** + * @brief 添加命令参数 + * @param command 命令指针 + * @param long_opt 长选项名 + * @param short_opt 短选项名 + * @param default_val 默认值 + * @param help 选项帮助信息 + * @param required 是否为必选参数 + * @param value_type 值类型 + * @return CommandArgs* 参数指针 + */ +CommandArgs *argParseAddArg(Command *command, + char *short_opt, + char *long_opt, + char *help, + char *default_val, + ArgParseCallback callback, + bool required, + ArgParseValueType value_type); + +/** + * @brief 添加全局参数 + * @param argParse 解析器指针 + * @param short_opt 短选项名 + * @param long_opt 长选项名 + * @param default_val 默认值 + * @param help 选项帮助信息 + * @param required 是否为必选参数 + * @param value_type 值类型 + * @return + */ +CommandArgs *argParseAddGlobalArg(ArgParse *argParse, + char *short_opt, + char *long_opt, + char *help, + char *default_val, + ArgParseCallback callback, + bool required, + ArgParseValueType value_type); + +/** + * @brief 禁用自动生成帮助信息 + */ +void argParseDisableAutoHelp(); + +/** End---------------构造API---------------- */ + +/** Start----------------解析API---------------- */ + +/** + * @brief 解析命令行参数 + * @param argParse 解析器指针 + * @param argc 参数个数 + * @param argv 参数列表 + */ +void argParseParse(ArgParse *argParse, int argc, char *argv[]); + +/** + * @brief 获取当前解析到的命令名 + * @param argParse 解析器指针 + * @return char* 命令名 + */ +char *argParseGetCurCommandName(ArgParse *argParse); + +/** + * @brief 获取当前解析到的命令的值,针对带值参数 + * @param argParse 解析器指针 + * @return char* 命令值 + */ +char *argParseGetCurCommandValue(ArgParse *argParse); +/** + * @brief 获取当前解析到的命令的值列表,针对带值参数 + * @param argParse 解析器指针 + * @param len 值个数buffer + * @return char** 命令值列表 + */ +char **argParseGetCurCommandValues(ArgParse *argParse, int *len); + +/** + * @brief + * 获取当前解析到的命令的参数,当前命令为解析到的最后一个命令或者命令的子命令 + * @param argParse 解析器指针 + * @param opt 选项名 + * @return char* 选项值 + * @return + */ +char *argParseGetCurArg(ArgParse *argParse, char *opt); + +/** + * @brief 获取当前解析到的命令参数列表,仅适用于多值参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @param len 参数个数buffer + * @return char** 参数列表 + */ +char **argParseGetCurArgList(ArgParse *argParse, char *opt, int *len); + +/** + * @brief 获取全局参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @return char* 选项值 + */ +char *argParseGetGlobalArg(ArgParse *argParse, char *opt); + +/** + * @brief 获取全局参数列表,仅适用于多值参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @param len 参数个数buffer + * @return char** 参数列表 + */ +char **argParseGetGlobalArgList(ArgParse *argParse, char *opt, int *len); + +/** + * @brief 检查当前解析到的命令的某个参数是否被触发 + * @param argParse 解析器指针 + * @param opt 选项名 + * @return bool 是否被触发 + */ +bool argParseCheckCurArgTriggered(ArgParse *argParse, char *opt); + +/** + * @brief 检查当前解析到的命令是否被触发 + * @param argParse 解析器指针 + * @param command_name 命令名 + * @return bool 是否被触发 + */ +bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name); + +/** + * @brief 检查全局参数是否被触发 + * @param argParse 解析器指针 + * @param opt 选项名 + * @return bool 是否被触发 + */ +bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt); + +/** End----------------解析API---------------- */ + +/** + * @brief 生成命令帮助信息 + * @param command 命令指针 + * @return char* 命令帮助信息 + */ +char *argParseGenerateHelpForCommand(Command *command); + +/** + * @brief 生成全局参数帮助信息 + * @param argParse 解析器指针 + * @return char* 全局参数帮助信息 + */ +char *argParseGenerateHelp(ArgParse *argParse); + +/** + * @brief 生成选项错误信息 + * @param argParse 解析器指针 + * @param name 选项名 + * @param short_flag 是否为短选项 + * @return char* 选项错误信息 + */ +char * +argParseGenerateArgErrorMsg(ArgParse *argParse, char *name, bool short_flag); + +_Noreturn void argParseError(ArgParse *argParse, + Command *lastCommand, + char *prefix, + char *suffix); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // CARGPARSE_H \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bc11a3c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "cargparse" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "conan>=2.18.1", +] diff --git a/src/ArgParse.c b/src/ArgParse.c new file mode 100644 index 0000000..a1a1f5a --- /dev/null +++ b/src/ArgParse.c @@ -0,0 +1,874 @@ +#include "ArgParse.h" +#include "ArgParseTools.h" +#include +#include +#include +#include +#include +#include + +static bool _AutoHelp = true; // 是否自动添加帮助信息 +static bool _COLOR = true; // 是否启用颜色 + +void argParseDisableAutoHelp() { _AutoHelp = false; } + +ArgParse *argParseInit(char *documentation) { + ArgParse *argParse = malloc(sizeof(ArgParse)); + if (argParse == NULL) { + return NULL; + } + + argParse->commands = NULL; + argParse->commands_len = 0; + argParse->current_command = NULL; + argParse->global_args = NULL; + argParse->global_args_len = 0; + argParse->argc = 0; + argParse->argv = NULL; + argParse->documentation = stringNewCopy(documentation); + + argParseAutoHelp(argParse); + + return argParse; +} + +/** + * @brief 自动帮助信息回调函数 + */ +int __helpCallback(ArgParse *argParse, char **val, int val_len) { + if (argParse == NULL) { + return -1; + } + char *help_doc = argParseGenerateHelp(argParse); + printf("%s\n", help_doc); + free(help_doc); + return 0; +} + +/** + * @brief 自动添加帮助信息 + * @param argParse ArgParse结构体指针 + */ +void argParseAutoHelp(ArgParse *argParse) { + if (!_AutoHelp) { + return; + } + if (argParse == NULL) { + return; + } + + argParseAddGlobalArg(argParse, + "-h", + "--help", + "show help", + NULL, + __helpCallback, + false, + NOVALUE); +} + +int __commandHelpCallback(ArgParse *argParse, char **val, int val_len) { + if (argParse == NULL) { + return -1; + } + char *help_doc = argParseGenerateHelpForCommand(argParse->current_command); + printf("%s", help_doc); + free(help_doc); + exit(0); + return 0; +} + +void argParseCommandAutoHelp(Command *command) { + if (!_AutoHelp) { + return; + } + if (command == NULL) { + return; + } + + argParseAddArg(command, + "-h", + "--help", + "show help", + NULL, + __commandHelpCallback, + false, + NOVALUE); +} + +Command *argParseAddCommand(ArgParse *argParse, + char *name, + char *help, + char *default_val, + ArgParseCallback callback, + CommandGroup *group, + ArgParseValueType value_type) { + + if (argParse == NULL || name == NULL) { + return NULL; + } + // 构造命令结构 + Command *command = + createCommand(name, help, default_val, callback, group, value_type); + + // 将命令结构添加到argParse中 + argParse->commands = realloc( + argParse->commands, sizeof(Command *) * (argParse->commands_len + 1)); + if (argParse->commands == NULL) { + return NULL; + } + argParse->commands[argParse->commands_len] = command; + argParse->commands_len++; + + argParseCommandAutoHelp(command); + + return command; +} + +/** + * @brief 添加子命令 + * @param Parent 父命令 + * @param name 子命令名称 + * @param help 子命令帮助信息 + * @param callback 子命令回调函数 + * @param group 子命令组 + * @param value_type 子命令值类型 + * @return + */ +Command *argParseAddSubCommand(Command *Parent, + char *name, + char *help, + char *default_val, + ArgParseCallback callback, + CommandGroup *group, + ArgParseValueType value_type) { + + if (Parent == NULL || name == NULL) { + return NULL; + } + // 构造命令结构 + Command *command = + createCommand(name, help, default_val, callback, group, value_type); + if (command == NULL) { + return NULL; + } + // 将命令结构添加到Parent中 + Parent->sub_commands = + realloc(Parent->sub_commands, + sizeof(Command *) * (Parent->sub_commands_len + 1)); + if (Parent->sub_commands == NULL) + return NULL; + Parent->sub_commands[Parent->sub_commands_len] = command; + Parent->sub_commands_len++; + + argParseCommandAutoHelp(command); + + return command; +} + +CommandArgs *argParseAddArg(Command *command, + char *short_opt, + char *long_opt, + char *help, + char *default_val, + ArgParseCallback callback, + bool required, + ArgParseValueType value_type) { + + if (command == NULL) { + return NULL; + } + // 构造参数结构 + CommandArgs *arg = createCommandArgs( + short_opt, long_opt, default_val, help, callback, required, value_type); + if (arg == NULL) { + return NULL; + } + // 将参数结构添加到command中 + command->args = + realloc(command->args, sizeof(CommandArgs *) * (command->args_len + 1)); + if (command->args == NULL) { + return NULL; + } + command->args[command->args_len] = arg; + command->args_len++; + + return arg; +} + +CommandArgs *argParseAddGlobalArg(ArgParse *argParse, + char *short_opt, + char *long_opt, + char *help, + char *default_val, + ArgParseCallback callback, + bool required, + ArgParseValueType value_type) { + + if (argParse == NULL) { + return NULL; + } + // 构造参数结构 + CommandArgs *arg = createCommandArgs( + short_opt, long_opt, default_val, help, callback, required, value_type); + if (arg == NULL) { + return NULL; + } + // 将参数结构添加到argParse中 + argParse->global_args = + realloc(argParse->global_args, + sizeof(CommandArgs *) * (argParse->global_args_len + 1)); + if (argParse->global_args == NULL) { + return NULL; + } + argParse->global_args[argParse->global_args_len] = arg; + argParse->global_args_len++; + + return arg; +} + +/** Start----------------内存释放API---------------- */ +void __freeCommandArgs(CommandArgs *arg) { + if (arg == NULL) { + return; + } + + free(arg->short_opt); + free(arg->long_opt); + free(arg->help); + free(arg->default_val); + for (size_t i = 0; i < arg->val_len; i++) { + free(arg->val[i]); + } + free(arg->val); + free(arg); +} + +void __freeCommand(Command *command) { + if (command == NULL) { + return; + } + + for (size_t i = 0; i < command->args_len; i++) { + __freeCommandArgs(command->args[i]); + } + + for (size_t i = 0; i < command->sub_commands_len; i++) { + __freeCommand(command->sub_commands[i]); + } + free(command->sub_commands); + free(command->name); + free(command->help); + free(command->default_val); + free(command); +} + +void argParseFree(ArgParse *argParse) { + // 释放命令及其子命令与参数 + for (size_t i = 0; i < argParse->commands_len; i++) { + __freeCommand(argParse->commands[i]); + } + free(argParse->commands); + + // 释放全局参数 + for (size_t i = 0; i < argParse->global_args_len; i++) { + __freeCommandArgs(argParse->global_args[i]); + } + free(argParse->global_args); + free(argParse); +} +/** End----------------内存释放API---------------- */ + +/** Start----------------解析API---------------- */ + +/** + * @brief 解析命令行参数 + * @param argParse ArgParse结构体指针 + * @param command 命令结构体指针 + * @param arg 参数结构体指针 + * @param arg_index 参数索引 + * @return int 返回解析到的参数索引 + */ +int __processArgs(ArgParse *argParse, CommandArgs *arg, int arg_index) { + arg->is_trigged = true; // 标记参数被触发 + + int current_index = arg_index; + + if (arg->value_type == MULTIVALUE) { + for (int i = arg_index + 1; i < argParse->argc; i++) { + if (checkArgType(argParse->argv[i]) == + COMMAND) { // COMMAND是无--或-开头的字符串,也可认定为参数值 + argParseSetArgVal(arg, argParse->argv[i]); + current_index = i; + } else { + current_index = i - 1; + break; + } + } + } else if (arg->value_type == SINGLEVALUE) { + if (arg_index + 1 < argParse->argc) { + argParseSetArgVal(arg, argParse->argv[arg_index + 1]); + current_index = arg_index + 1; + } + } else if (arg->value_type == NOVALUE) { + current_index = arg_index; + } + + if (arg->callback != NULL) { + arg->callback(argParse, arg->val, arg->val_len); + } + + return current_index; +} + +// 处理子命令参数 +int __processSubCommand(ArgParse *argParse, + Command *ParentCommand, + char *name, + int command_index) { + + Command *sub_command = argParseFindSubCommand(ParentCommand, name); + if (sub_command == NULL) { + argParseError(argParse, argParse->current_command, NULL, NULL); + return -1; + } + CommandArgs *arg = NULL; + + argParse->current_command = sub_command; + + for (int i = command_index + 1; i < argParse->argc; i++) { + ArgType argType = checkArgType(argParse->argv[i]); + switch (argType) { + case COMMAND: + return __processSubCommand( + argParse, sub_command, argParse->argv[i], i); // 递归处理子命令 + case LONG_ARG: + arg = + argParseFindCommandArgs(sub_command, argParse->argv[i], false); + if (arg != NULL) { + i = __processArgs(argParse, arg, i); + } + return i; + case SHORT_ARG: + arg = argParseFindCommandArgs(sub_command, argParse->argv[i], true); + if (arg != NULL) { + i = __processArgs(argParse, + arg, + i); // 解析参数值并返回以解析到的索引位置 + return i; + } else { + argParseError(argParse, argParse->current_command, NULL, NULL); + return -1; + } + default: + argParseError(argParse, argParse->current_command, NULL, NULL); + return -1; + } + } + argParseError(argParse, argParse->current_command, NULL, NULL); + return -1; +} + +// 处理命令参数 + +int __processCommand(ArgParse *argParse, char *name, int command_index) { + Command *command = argParseFindCommand(argParse, name); + if (command == NULL) { + + argParseError(argParse, command, "Command not found", NULL); + return -1; + } + + command->is_trigged = true; // 标记命令被触发 + + CommandArgs *arg = NULL; + argParse->current_command = command; + + for (int i = command_index + 1; i < argParse->argc; i++) { + ArgType argType = checkArgType(argParse->argv[i]); + switch (argType) { + case COMMAND: + // 命令无值则处理子命令 + if (command->value_type == NOVALUE) { + __processSubCommand(argParse, command, argParse->argv[i], i); + return argParse->argc - 1; + } else { + // 命令有值,则认为该值是命令值 + argParseSetCommandVal(command, argParse->argv[i]); + } + break; + case LONG_ARG: + // 处理命令长选项 + arg = argParseFindCommandArgs(command, argParse->argv[i], false); + if (arg == NULL) { + // 可能全局参数被放在了命令参数之前 + arg = + argParseFindGlobalArgs(argParse, argParse->argv[i], false); + if (arg == NULL) { + char *msg = argParseGenerateArgErrorMsg( + argParse, argParse->argv[i], false); + argParseError( + argParse, argParse->current_command, msg, NULL); + return -1; + } + i = __processArgs(argParse, arg, i); + break; + } + i = __processArgs(argParse, arg, i); + break; + case SHORT_ARG: + // 处理命令短选项 + arg = argParseFindCommandArgs(command, argParse->argv[i], true); + if (arg == NULL) { + // 可能全局参数被放在了命令参数之前 + arg = argParseFindGlobalArgs(argParse, argParse->argv[i], true); + if (arg == NULL) { + char *msg = argParseGenerateArgErrorMsg( + argParse, argParse->argv[i], true); + argParseError( + argParse, argParse->current_command, msg, NULL); + return -1; + } + i = __processArgs( + argParse, arg, i); // 解析参数值并返回以解析到的索引位置 + break; + } + i = __processArgs(argParse, + arg, + i); // 解析参数值并返回以解析到的索引位置 + break; + default: + argParseError(argParse, argParse->current_command, NULL, NULL); + return -1; + } + } + + return argParse->argc - 1; +} + +/** + * @brief 解析命令行参数 + * @param argParse 解析器指针 + * @param argc 参数个数 + * @param argv 参数列表 + */ +void argParseParse(ArgParse *argParse, int argc, char *argv[]) { + argParse->argc = argc; + argParse->argv = argv; + + CommandArgs *arg = NULL; + + for (int i = 1; i < argc; i++) { + ArgType argType = checkArgType(argv[i]); + switch (argType) { + case COMMAND: + i = __processCommand(argParse, argv[i], i); + break; + case LONG_ARG: // 处理全局长选项 + arg = argParseFindGlobalArgs(argParse, argv[i], false); + if (arg == NULL) { + char *msg = + argParseGenerateArgErrorMsg(argParse, argv[i], false); + argParseError(argParse, argParse->current_command, msg, NULL); + return; // 错误处理 + } + i = __processArgs(argParse, arg, i); + break; + case SHORT_ARG: // 处理全局短选项 + arg = argParseFindGlobalArgs(argParse, argv[i], true); + if (arg == NULL) { + char *msg = + argParseGenerateArgErrorMsg(argParse, argv[i], true); + argParseError(argParse, argParse->current_command, msg, NULL); + return; // 错误处理 + } + i = __processArgs( + argParse, arg, i); // 解析参数值并返回以解析到的索引位置 + break; + default: + break; + } + } + + // 执行当前命令的回调函数 + if (argParse->current_command != NULL && + argParse->current_command->is_trigged && + argParse->current_command->callback != NULL) { + argParse->current_command->callback(argParse, + argParse->current_command->val, + argParse->current_command->val_len); + } +} + +/** + * @brief 获取当前解析到的命令名,会返回最后一个解析到的命令名(子命令) + * @param argParse 解析器指针 + * @return char* 命令名 + */ +char *argParseGetCurCommandName(ArgParse *argParse) { + if (argParse == NULL) + return NULL; + + if (argParse->current_command == NULL) { + return NULL; + } + return argParse->current_command->name; +} + +/** + * @brief 获取当前解析到的命令参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @return char* 选项值 + * @return + */ +char *argParseGetCurArg(ArgParse *argParse, char *opt) { + CommandArgs *arg = NULL; + // 尝试获取当前命令的长选项参数 + arg = argParseFindCommandArgs(argParse->current_command, opt, false); + + if (arg == NULL) { + // 尝试获取当前命令的短选项参数 + arg = argParseFindCommandArgs(argParse->current_command, opt, true); + } + + if (arg == NULL) { + return NULL; + } + if (arg->val_len >= 1) { + return arg->val[0]; + } + return NULL; +} + +/** + * @brief 获取当前解析到的命令参数列表,仅适用于多值参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @param len 参数个数buffer + * @return char** 参数列表 + */ +char **argParseGetCurArgList(ArgParse *argParse, char *opt, int *len) { + CommandArgs *arg = NULL; + arg = argParseFindCommandArgs(argParse->current_command, opt, false); + + if (arg == NULL) { + arg = argParseFindCommandArgs(argParse->current_command, opt, true); + } + + if (arg == NULL) { + return NULL; + } + *len = arg->val_len; + return arg->val; +} + +/** + * @brief 获取全局参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @return char* 选项值 + */ +char *argParseGetGlobalArg(ArgParse *argParse, char *opt) { + CommandArgs *arg = NULL; + arg = argParseFindGlobalArgs(argParse, opt, false); + + if (arg == NULL) { + arg = argParseFindGlobalArgs(argParse, opt, true); + } + + if (arg == NULL) { + return NULL; + } + if (arg->val_len >= 1) { + return arg->val[0]; + } + return NULL; +} + +/** + * @brief 获取当前解析到的命令参数值 + * @return char* 参数值 + */ +char *argParseGetCurCommandValue(ArgParse *argParse) { + Command *command = argParse->current_command; + if (command == NULL) { + return NULL; + } + if (command->val_len >= 1) { + return command->val[0]; + } + return NULL; +} +/** + * @brief 获取当前解析到的命令参数列表,仅适用于多值参数 + * @return char** 参数列表 + */ +char **argParseGetCurCommandValues(ArgParse *argParse, int *len) { + Command *command = argParse->current_command; + if (command == NULL) { + return NULL; + } + if (command->val_len >= 1) { + *len = command->val_len; + return command->val; + } + return NULL; +} + +/** + * @brief 获取全局参数列表,仅适用于多值参数 + * @param argParse 解析器指针 + * @param opt 选项名 + * @param len 参数个数buffer + * @return char** 参数列表 + */ +char **argParseGetGlobalArgList(ArgParse *argParse, char *opt, int *len) { + CommandArgs *arg = NULL; + arg = argParseFindGlobalArgs(argParse, opt, false); + + if (arg == NULL) { + arg = argParseFindGlobalArgs(argParse, opt, true); + } + + if (arg == NULL) { + return NULL; + } + *len = arg->val_len; + return arg->val; +} + +size_t __getStrlen(char *str) { + if (str == NULL) { + return 0; + } + return strlen(str); +} + +void __catStr(char **dst, int count, ...) { + va_list args; + va_start(args, count); + + size_t total_len = 0; + total_len = __getStrlen(*dst); + + // 计算总长度 + char *temp = NULL; + for (int i = 0; i < count; i++) { + temp = va_arg(args, char *); + total_len += __getStrlen(temp); + } + va_end(args); + + // 分配内存 + *dst = realloc(*dst, total_len + 1); // +1 是为了存储字符串结束符 '\0' + if (*dst == NULL) { + va_end(args); + return; // 处理内存分配失败 + } + + // 拼接字符串 + va_start(args, count); + temp = NULL; + for (int i = 0; i < count; i++) { + temp = va_arg(args, char *); + strcat(*dst, temp); + } + + va_end(args); +} + +char *argParseGenerateHelpForCommand(Command *command) { + if (command == NULL) { + return NULL; + } + + char *help_msg = stringNewCopy(command->help); + if (help_msg == NULL) { + return NULL; + } + + __catStr(&help_msg, 1, "\n\n"); + __catStr(&help_msg, 2, "\033[1;33mUsage\033[0m: ", command->name); + + switch (command->value_type) { + case NOVALUE: + break; + case SINGLEVALUE: + __catStr(&help_msg, 1, " "); + break; + case MULTIVALUE: + __catStr(&help_msg, 1, " ..."); + } + + if (command->args != NULL) { + __catStr(&help_msg, 1, " [Options]"); + __catStr(&help_msg, 1, "\n\n\033[1;34mOptions\033[0m:"); + for (int i = 0; i < command->args_len; i++) { + CommandArgs *arg = command->args[i]; + + __catStr(&help_msg, + 6, + "\n \033[1;32m", + arg->short_opt, + "\033[0m , \033[1;32m", + arg->long_opt, + "\033[0m ", + arg->help); + } + } + __catStr(&help_msg, 1, "\n"); + + return help_msg; +} + +// 检查全局参数是否被触发 +bool argParseCheckGlobalTriggered(ArgParse *argParse, char *opt) { + CommandArgs *arg = NULL; + arg = argParseFindGlobalArgs(argParse, opt, false); + + if (arg == NULL) { + arg = argParseFindGlobalArgs(argParse, opt, true); + } + + if (arg == NULL) { + return false; + } + return arg->is_trigged; +} +// 检测当前命令的某项参数是否被触发 +bool argParseCheckCurArgTriggered(ArgParse *argParse, char *opt) { + CommandArgs *arg = NULL; + arg = argParseFindCommandArgs(argParse->current_command, opt, false); + + if (arg == NULL) { + arg = argParseFindCommandArgs(argParse->current_command, opt, true); + } + + if (arg == NULL) { + return false; + } + return arg->is_trigged; +} +// 检测指定命令是否被触发 +bool argParseCheckCommandTriggered(ArgParse *argParse, char *command_name) { + Command *command = argParseFindCommand(argParse, command_name); + if (command == NULL) { + return false; + } + return command->is_trigged; +} + +_Noreturn void argParseError(ArgParse *argParse, + Command *lastCommand, + char *prefix, + char *suffix) { + if (argParse == NULL) { + printf("ERROR: Parse is NULL\n"); + exit(1); + } + if (lastCommand == NULL) { + char *help = argParseGenerateHelp(argParse); + printf("\033[1;31mERROR\033[0m: Last command is unknown\n"); + printf("%s\n", help); + exit(1); + } + + char *ErrorMsg = NULL; + + if (prefix != NULL) { + ErrorMsg = stringNewCopy(prefix); + } + + char *command_help_msg = argParseGenerateHelpForCommand(lastCommand); + int command_help_msg_len = strlen(command_help_msg); + + if (command_help_msg != NULL) { + __catStr(&ErrorMsg, 2, "\n", command_help_msg); + } + + if (suffix != NULL) { + __catStr(&ErrorMsg, 2, "\n", suffix); + } + + printf("%s\n", ErrorMsg); + free(ErrorMsg); + free(command_help_msg); + free(prefix); + free(suffix); + exit(1); +} + +char * +argParseGenerateArgErrorMsg(ArgParse *argParse, char *name, bool short_flag) { + if (argParse == NULL) { + return NULL; + } + if (name == NULL) { + return NULL; + } + + char *ErrorMsg = NULL; + + if (_COLOR) { + ErrorMsg = stringNewCopy("\033[1;31mERROR\033[0m: Invalid argument "); + } else { + ErrorMsg = stringNewCopy("ERROR: Invalid argument "); + } + + if (short_flag) { + __catStr(&ErrorMsg, 1, name); + } else { + __catStr(&ErrorMsg, 1, name); + } + return ErrorMsg; +} + +char *argParseGenerateHelp(ArgParse *argParse) { + if (argParse == NULL) { + return NULL; + } + + char *help_msg = stringNewCopy(argParse->documentation); + + if (help_msg == NULL) { + return NULL; + } + + __catStr(&help_msg, 1, "\n\n\033[1;34mCommands\033[0m:"); + for (int i = 0; i < argParse->commands_len; i++) { + Command *command = argParse->commands[i]; + + __catStr(&help_msg, + 4, + "\n \033[1;32m", + command->name, + "\033[0m ", + command->help); + } + + if (argParse->global_args_len > 0) { + __catStr(&help_msg, 1, "\n\n\033[1;34mGlobal Options\033[0m:"); + for (int i = 0; i < argParse->global_args_len; i++) { + CommandArgs *arg = argParse->global_args[i]; + + __catStr(&help_msg, + 6, + "\n \033[1;32m", + arg->short_opt, + "\033[0m , \033[1;32m", + arg->long_opt, + "\033[0m ", + arg->help); + } + } + + return help_msg; +} + +_Noreturn void argParseHelp(ArgParse *argParse) { + char *help_msg = argParseGenerateHelp(argParse); + printf("%s\n", help_msg); + free(help_msg); + exit(0); +} \ No newline at end of file diff --git a/src/ArgParseTools.c b/src/ArgParseTools.c new file mode 100644 index 0000000..59f4a30 --- /dev/null +++ b/src/ArgParseTools.c @@ -0,0 +1,245 @@ +#include "ArgParseTools.h" +#include "ArgParse.h" +#include +#include +#include + +/** + * @brief 分配新内存复制字符串 + * @param str 字符串 + * @return 复制后的字符串内存指针 + */ +char *stringNewCopy(char *str) { + if (str == NULL) { + return NULL; + } + char *mem = malloc(strlen(str) + 1); + if (mem == NULL) { + return NULL; + } + strcpy(mem, str); + return mem; +} + +// 构造命令 +Command *createCommand(char *name, + char *help, + char *default_val, + ArgParseCallback callback, + CommandGroup *group, + ArgParseValueType value_typ) { + // 构造命令结构 + Command *command = malloc(sizeof(Command)); + if (command == NULL || name == NULL) { + return NULL; + } + + command->name = stringNewCopy(name); + if (command->name == NULL) { + return NULL; + } + + command->help = stringNewCopy(help); + command->default_val = stringNewCopy(default_val); + command->callback = callback; + + command->sub_commands = NULL; + command->sub_commands_len = 0; + command->args = NULL; + command->args_len = 0; + command->value_type = value_typ; + + if (group != NULL) { + command->group = group; + } else { + command->group = NULL; + } + + command->val = NULL; + command->val_len = 0; + command->is_trigged = false; // 是否被触发 + return command; +} + +ArgType checkArgType(char *arg) { + size_t len = strlen(arg); + if (len == 0) { + return BAD; + } + if (arg[0] == '-') { + if (len >= 2) { + if (arg[1] == '-') { + return LONG_ARG; + } else { + return SHORT_ARG; + } + } + } else { + return COMMAND; + } + return BAD; +} + +// 寻找命令 +Command *argParseFindCommand(ArgParse *argParse, char *name) { + for (size_t i = 0; i < argParse->commands_len; i++) { + if (strcmp(argParse->commands[i]->name, name) == 0) { + return argParse->commands[i]; + } + } + return NULL; +} + +Command *argParseFindSubCommand(Command *command, char *name) { + for (size_t i = 0; i < command->sub_commands_len; i++) { + if (strcmp(command->sub_commands[i]->name, name) == 0) { + return command->sub_commands[i]; + } + } + return NULL; +} + +CommandArgs *createCommandArgs(char *short_opt, + char *long_opt, + char *default_val, + char *help, + ArgParseCallback callback, + bool required, + ArgParseValueType value_type) { + CommandArgs *args = malloc(sizeof(CommandArgs)); + + if (short_opt == NULL && long_opt == NULL) { + return NULL; + } + + // 分配内存 + args->short_opt = stringNewCopy(short_opt); + args->long_opt = stringNewCopy(long_opt); + args->default_val = stringNewCopy(default_val); + args->help = stringNewCopy(help); + + args->callback = callback; + args->required = required; + args->value_type = value_type; + + // 解析属性初始化 + args->val = NULL; + args->val_len = 0; + args->is_trigged = false; // 是否被触发 + + return args; +} + +CommandArgs * +argParseFindCommandArgs(Command *command, char *name, bool short_flag) { + if (command == NULL) { + return NULL; + } + + for (size_t i = 0; i < command->args_len; i++) { + if (short_flag) { + if (command->args[i]->short_opt != NULL && + strcmp(command->args[i]->short_opt, name) == 0) { + return command->args[i]; + } + } else { + if (command->args[i]->long_opt != NULL && + strcmp(command->args[i]->long_opt, name) == 0) { + return command->args[i]; + } + } + } + return NULL; +} + +CommandArgs * +argParseFindGlobalArgs(ArgParse *argParse, char *name, bool short_flag) { + for (size_t i = 0; i < argParse->global_args_len; i++) { + if (short_flag) { + if (argParse->global_args[i]->short_opt != NULL && + strcmp(argParse->global_args[i]->short_opt, name) == 0) { + return argParse->global_args[i]; + } + } else { + if (argParse->global_args[i]->long_opt != NULL && + strcmp(argParse->global_args[i]->long_opt, name) == 0) { + return argParse->global_args[i]; + } + } + } + return NULL; +} + +/** + * @brief 设置命令参数值 + * @param args 参数 + * @param val 值 + * @return 成功返回true,失败返回false + */ +bool argParseSetArgVal(CommandArgs *args, char *val) { + if (args->value_type == MULTIVALUE) { // 多值 + args->val = realloc(args->val, (args->val_len + 1) * sizeof(char *)); + if (args->val == NULL) { + return false; + } + args->val[args->val_len] = stringNewCopy(val); + if (args->val[args->val_len] == NULL) { + return false; + } + args->val_len++; + return true; + } else if (args->value_type == SINGLEVALUE) { // 单值 + if (args->val != NULL) { + free(args->val); + } + args->val = malloc(sizeof(char *)); + + if (args->val == NULL) { + return false; + } + args->val[0] = stringNewCopy(val); // 分配内存 + if (args->val[0] == NULL) { + return false; + } + args->val_len = 1; + return true; + } else if (args->value_type == NOVALUE) { // 无值 + return true; + } + + return false; +} + +bool argParseSetCommandVal(Command *command, char *val) { + if (command->value_type == MULTIVALUE) { // 多值 + command->val = + realloc(command->val, (command->val_len + 1) * sizeof(char *)); + if (command->val == NULL) { + return false; + } + command->val[command->val_len] = stringNewCopy(val); + if (command->val[command->val_len] == NULL) { + return false; + } + command->val_len++; + return true; + } else if (command->value_type == SINGLEVALUE) { // 单值 + if (command->val != NULL) { + free(command->val); + } + command->val = malloc(sizeof(char *)); + + if (command->val == NULL) { + return false; + } + command->val[0] = stringNewCopy(val); // 分配内存 + if (command->val[0] == NULL) { + return false; + } + command->val_len = 1; + return true; + } else if (command->value_type == NOVALUE) { // 无值 + return true; + } + return false; +} \ No newline at end of file diff --git a/src/ArgParseTools.h b/src/ArgParseTools.h new file mode 100644 index 0000000..d5a9d74 --- /dev/null +++ b/src/ArgParseTools.h @@ -0,0 +1,67 @@ +#ifndef ARGPARSETOOLS_H +#define ARGPARSETOOLS_H + +#include "ArgParse.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LONG_ARG, // 长选项 + SHORT_ARG, // 短选项 + COMMAND, // 命令 + BAD, // 错误 +} ArgType; + +Command *createCommand(char *name, + char *help, + char *default_val, + ArgParseCallback callback, + CommandGroup *group, + ArgParseValueType value_type); // 创建命令 + +CommandGroup *createCommandGroup(char *name, char *help); // 创建命令组 + +CommandGroup *addCommandToGroup(CommandGroup *group, + Command *command); // 添加命令到命令组 + +CommandArgs *createCommandArgs(char *short_opt, + char *long_opt, + char *default_val, + char *help, + ArgParseCallback callback, + bool required, + ArgParseValueType value_type); // 创建命令参数 + +/** + * @brief 设置命令参数值 + * @param args 参数 + * @param val 值 + * @return 成功返回true,失败返回false + */ +bool argParseSetArgVal(CommandArgs *args, char *val); // 设置命令参数值 + +bool argParseSetCommandVal(Command *command, char *val); // 设置命令值 + +ArgType checkArgType(char *arg); // 检查参数类型 + +Command *argParseFindCommand(ArgParse *argParse, char *name); // 查找命令 + +Command *argParseFindSubCommand(Command *command, char *name); // 查找子命令 + +CommandArgs *argParseFindCommandArgs(Command *command, + char *name, + bool short_flag); // 查找命令参数 + +CommandArgs *argParseFindGlobalArgs(ArgParse *argParse, + char *name, + bool short_flag); // 查找全局参数 + +char *stringNewCopy(char *str); // 创建字符串副本 + +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // ARGPARSETOOLS_H \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..fd33004 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,34 @@ +project(CArgParseTest_) + +#单命令的单一选项多值测试 +add_executable(${PROJECT_NAME}single_arg test_single_arg.c) +target_link_libraries(${PROJECT_NAME}single_arg CArgParse) +add_test(${PROJECT_NAME}single_arg ${PROJECT_NAME}single_arg install -p testpackge1 testpackge2 testpackge3) + +# 单命令的多选项测试 +add_executable(${PROJECT_NAME}mult_arg test_mult_arg.c) +target_link_libraries(${PROJECT_NAME}mult_arg CArgParse) +add_test(${PROJECT_NAME}mult_arg ${PROJECT_NAME}mult_arg install -p testpackge1 testpackge2 testpackge3 -i www.test.com -f file1.txt file2.txt) + +# 全局选项测试 +add_executable(${PROJECT_NAME}global_arg test_global_arg.c) +target_link_libraries(${PROJECT_NAME}global_arg CArgParse) +add_test(${PROJECT_NAME}global_arg ${PROJECT_NAME}global_arg -v install -p testpackge1 testpackge2 testpackge3 -q) + +# 子命令测试 +add_executable(${PROJECT_NAME}subcommand test_subcommand.c) +target_link_libraries(${PROJECT_NAME}subcommand CArgParse) +add_test(${PROJECT_NAME}subcommand ${PROJECT_NAME}subcommand -v install tools -t tool1 tool2) + +# 未知命令测试 +add_executable(${PROJECT_NAME}unknow_command test_unknow_command.c) +target_link_libraries(${PROJECT_NAME}unknow_command CArgParse) +add_test(${PROJECT_NAME}unknow_command ${PROJECT_NAME}unknow_command unknow) +set_tests_properties(${PROJECT_NAME}unknow_command PROPERTIES WILL_FAIL TRUE) +set_tests_properties(${PROJECT_NAME}unknow_command PROPERTIES FAIL_REGULAR_EXPRESSION "ERROR: Last command is not found") + +# 未知命令选项测试,预期打印该命令的帮助信息 +add_executable(${PROJECT_NAME}unknow_command_arg test_unknow_command.c) +target_link_libraries(${PROJECT_NAME}unknow_command_arg CArgParse) +add_test(${PROJECT_NAME}unknow_command_arg ${PROJECT_NAME}unknow_command_arg install --unknow) +set_tests_properties(${PROJECT_NAME}unknow_command_arg PROPERTIES WILL_FAIL TRUE) diff --git a/tests/initArgParse.h b/tests/initArgParse.h new file mode 100644 index 0000000..2589732 --- /dev/null +++ b/tests/initArgParse.h @@ -0,0 +1,91 @@ +#include "ArgParse.h" +#include +#include +#include + +ArgParse *initArgParse() { + ArgParse *argparse = argParseInit("测试程序"); + Command *command = NULL; + Command *sub_command = NULL; + + // add global arguments + argParseAddGlobalArg(argparse, + "-v", + "--version", + "Show version", + NULL, + NULL, + false, + NOVALUE); + argParseAddGlobalArg( + argparse, "-q", "--quiet", "Quiet mode", NULL, NULL, false, NOVALUE); + + // add arguments + command = argParseAddCommand( + argparse, "install", "Install the package", NULL, NULL, NULL, NOVALUE); + argParseAddArg(command, + "-i", + "--index", + "Index URL", + "https://example.com", + NULL, + false, + SINGLEVALUE); + argParseAddArg(command, + "-f", + "--file", + "Package file", + "package.json", + NULL, + false, + MULTIVALUE); + argParseAddArg(command, + "-p", + "--package", + "Package file", + "package.json", + NULL, + false, + MULTIVALUE); + + sub_command = argParseAddSubCommand( + command, "tools", "Install tools", NULL, NULL, NULL, MULTIVALUE); + + argParseAddArg(sub_command, + "-t", + "--tool", + "Tool name", + "Tool name", + NULL, + true, + MULTIVALUE); + sub_command = argParseAddSubCommand( + command, "tools_sub", "Install tools", NULL, NULL, NULL, MULTIVALUE); + + argParseAddArg(sub_command, + "-s", + "--source", + "test_source", + "tools subcommand test", + NULL, + true, + MULTIVALUE); + + command = argParseAddCommand(argparse, + "uninstall", + "Uninstall the package", + NULL, + NULL, + NULL, + SINGLEVALUE); + argParseAddArg(command, + "-p", + "--package", + "Package name", + "Package name", + NULL, + true, + MULTIVALUE); + + return argparse; +} \ No newline at end of file diff --git a/tests/test_global_arg.c b/tests/test_global_arg.c new file mode 100644 index 0000000..a9cf5e0 --- /dev/null +++ b/tests/test_global_arg.c @@ -0,0 +1,39 @@ +#include "ArgParse.h" +#include "initArgParse.h" +#include +#include +#include + +int main(int argc, char *argv[]) { + ArgParse *argparse = initArgParse(); + + argParseParse(argparse, argc, argv); + + char *val = argParseGetGlobalArg(argparse, "-v"); + + if (argParseCheckGlobalTriggered(argparse, "-v")) { + printf("Global arg -v triggered\n"); + } + + if (val != NULL) { + printf("Global arg -v: %s\n", val); + } else { + printf("Global arg -v is NULL\n"); + } + + char *val_q = argParseGetGlobalArg(argparse, "-q"); + + if (argParseCheckGlobalTriggered(argparse, "-q")) { + printf("Global arg -q triggered\n"); + } + + if (val != NULL) { + printf("Global arg -q: %s\n", val); + } else { + printf("Global arg -q is NULL\n"); + } + + argParseFree(argparse); + + return 0; +} \ No newline at end of file diff --git a/tests/test_mult_arg.c b/tests/test_mult_arg.c new file mode 100644 index 0000000..d521abb --- /dev/null +++ b/tests/test_mult_arg.c @@ -0,0 +1,45 @@ +#include "ArgParse.h" +#include "initArgParse.h" +#include +#include + +int main(int argc, char *argv[]) { + ArgParse *argparse = initArgParse(); + + argParseParse(argparse, argc, argv); + + const char *testv[3] = {"testpackge1", "testpackge2", "testpackge3"}; + + + // Test -p + char *val = argParseGetCurArg(argparse, "-p"); + + int len = 0; + char **vals = argParseGetCurArgList(argparse, "-p", &len); + + for (int i = 0; i < len; i++) { + printf("-p Value: %s\n", vals[i]); + assert(strcmp(vals[i], testv[i]) == 0); + } + + // Test -i + char *val_i = argParseGetCurArg(argparse, "-i"); + printf("-i Value: %s\n", val_i); + assert(strcmp(val_i, "www.test.com") == 0); + + + + // Test -f + len = 0; + const char *testf[2] = {"file1.txt", "file2.txt"}; + char **val_f = argParseGetCurArgList(argparse, "-f", &len); + for (int i = 0; i < len; i++) { + printf("-f Value: %s\n", val_f[i]); + assert(strcmp(val_f[i], testf[i]) == 0); + } + + + argParseFree(argparse); + + return 0; +} \ No newline at end of file diff --git a/tests/test_single_arg.c b/tests/test_single_arg.c new file mode 100644 index 0000000..b99f859 --- /dev/null +++ b/tests/test_single_arg.c @@ -0,0 +1,26 @@ +#include "ArgParse.h" +#include "initArgParse.h" +#include +#include + +int main(int argc, char *argv[]) { + ArgParse *argparse = initArgParse(); + + argParseParse(argparse, argc, argv); + + const char *testv[3] = {"testpackge1", "testpackge2", "testpackge3"}; + + char *val = argParseGetCurArg(argparse, "-p"); + + int len = 0; + char **vals = argParseGetCurArgList(argparse, "-p", &len); + + for (int i = 0; i < len; i++) { + printf("-p Value: %s\n", vals[i]); + assert(strcmp(vals[i], testv[i]) == 0); + } + + argParseFree(argparse); + + return 0; +} \ No newline at end of file diff --git a/tests/test_subcommand.c b/tests/test_subcommand.c new file mode 100644 index 0000000..7b877cd --- /dev/null +++ b/tests/test_subcommand.c @@ -0,0 +1,22 @@ +#include "ArgParse.h" +#include "initArgParse.h" +#include +#include + +int main(int argc, char *argv[]) { + ArgParse *argparse = initArgParse(); + + argParseParse(argparse, argc, argv); + + + char *command_name = argParseGetCurCommandName(argparse); + printf("command name: %s\n", command_name); + assert(strcmp(command_name, "tools") == 0); + + char * val = argParseGetCurArg(argparse, "-t"); + printf("tools -t: %s\n", val); + + argParseFree(argparse); + + return 0; +} \ No newline at end of file diff --git a/tests/test_unknow_command.c b/tests/test_unknow_command.c new file mode 100644 index 0000000..98da073 --- /dev/null +++ b/tests/test_unknow_command.c @@ -0,0 +1,14 @@ +#include "ArgParse.h" +#include "initArgParse.h" +#include + +int main(int argc, char *argv[]) { + ArgParse *argparse = initArgParse(); + + argParseParse(argparse, argc, argv); + + + argParseFree(argparse); + + return 0; +} \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..e606e68 --- /dev/null +++ b/uv.lock @@ -0,0 +1,206 @@ +version = 1 +revision = 2 +requires-python = ">=3.13" + +[[package]] +name = "cargparse" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "conan" }, +] + +[package.metadata] +requires-dist = [{ name = "conan", specifier = ">=2.18.1" }] + +[[package]] +name = "certifi" +version = "2025.7.9" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/8a/c729b6b60c66a38f590c4e774decc4b2ec7b0576be8f1aa984a53ffa812a/certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", size = 160386, upload-time = "2025-07-09T02:13:58.874Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/f3/80a3f974c8b535d394ff960a11ac20368e06b736da395b551a49ce950cce/certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39", size = 159230, upload-time = "2025-07-09T02:13:57.007Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "conan" +version = "2.18.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "colorama" }, + { name = "distro", marker = "platform_system == 'FreeBSD' or sys_platform == 'linux'" }, + { name = "fasteners" }, + { name = "jinja2" }, + { name = "patch-ng" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/55/9a10f21dd6dc9137c6e7b51eff7c54ab92400812e6e795d1a991ead00b54/conan-2.18.1.tar.gz", hash = "sha256:5d8e9fac7614de9297933f65de8f17db14851a871cebc962f4856b7c294f43c5", size = 521034, upload-time = "2025-07-04T07:08:07.842Z" } + +[[package]] +name = "distro" +version = "1.8.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/89/eaa3a3587ebf8bed93e45aa79be8c2af77d50790d15b53f6dfc85b57f398/distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8", size = 59428, upload-time = "2022-10-10T15:30:33.395Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/2c/c90a3adaf0ddb70afe193f5ebfb539612af57cffe677c3126be533df3098/distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff", size = 20315, upload-time = "2022-10-10T15:30:26.903Z" }, +] + +[[package]] +name = "fasteners" +version = "0.19" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/d4/e834d929be54bfadb1f3e3b931c38e956aaa3b235a46a3c764c26c774902/fasteners-0.19.tar.gz", hash = "sha256:b4f37c3ac52d8a445af3a66bce57b33b5e90b97c696b7b984f530cf8f0ded09c" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/bf/fd60001b3abc5222d8eaa4a204cd8c0ae78e75adc688f33ce4bf25b7fafa/fasteners-0.19-py3-none-any.whl", hash = "sha256:758819cb5d94cdedf4e836988b74de396ceacb8e2794d21f82d131fd9ee77237" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "patch-ng" +version = "1.18.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/c0/53a2f017ac5b5397a7064c2654b73c3334ac8461315707cbede6c12199eb/patch-ng-1.18.1.tar.gz", hash = "sha256:52fd46ee46f6c8667692682c1fd7134edc65a2d2d084ebec1d295a6087fc0291", size = 17913, upload-time = "2024-10-25T12:20:10.591Z" } + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "urllib3" +version = "2.0.7" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/47/b215df9f71b4fdba1025fc05a77db2ad243fa0926755a52c5e71659f4e3c/urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", size = 282546, upload-time = "2023-10-17T17:46:50.542Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/b2/b157855192a68541a91ba7b2bbcb91f1b4faa51f8bae38d8005c034be524/urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e", size = 124213, upload-time = "2023-10-17T17:46:48.538Z" }, +]