@@ -97,7 +97,11 @@ fun IRequest.getHtmlPrefix(): String {
9797 fun getColorStyle (color : String ) = " style='color:${color} '"
9898
9999 return """
100- <span ${getColorStyle(color)} >${httpStatusCode} </span> <span ${getColorStyle(secColor)} >${httpMethod} </span> <span ${getColorStyle(secColor)} >${durationMs} ms</span>
100+ <span ${getColorStyle(color)} >${httpStatusCode} </span> <span ${getColorStyle(secColor)} >${httpMethod} </span> <span ${
101+ getColorStyle(
102+ secColor
103+ )
104+ } >${durationMs} ms</span>
101105 """ .trimIndent()
102106}
103107
@@ -196,7 +200,7 @@ class HurlGenerate(private val request: IRequest) {
196200
197201 private fun getPostBody (): String {
198202 return when (val body = request.httpRequestBody) {
199- is String -> if (body.isNotBlank()) body else " "
203+ is String -> if (body.isNotBlank()) body else " "
200204 is Map <* , * > -> if (body.isNotEmpty()) GsonBuilder ().setPrettyPrinting().create().toJson(body) else " "
201205 else -> body?.toString() ? : " "
202206 }
@@ -217,8 +221,6 @@ fun formatSize(sizeInBytes: Long): String {
217221}
218222
219223
220-
221-
222224/* *
223225 * 将 IRequest 对象转换为与 Dart/Flutter DevTools "Copy as cURL" 功能风格完全一致的命令字符串。
224226 *
@@ -228,16 +230,34 @@ fun formatSize(sizeInBytes: Long): String {
228230 * - 精确的参数顺序 (`--request METHOD 'URL' ...`)。
229231 * - 自动过滤掉由 cURL 管理的冗余头信息 (Host, Content-Length, Accept-Encoding)。
230232 * - 格式化为易于阅读和粘贴的多行命令。
233+ * - 跨平台支持:自动适配 Windows (CMD/PowerShell) 和 Unix-like 系统的语法差异。
231234 *
232235 * @param gsonInstance 一个可选的 Gson 实例,用于序列化 JSON 请求体。如果为 null,将创建一个新的实例。
233236 * @return 格式化后的 cURL 命令字符串。
234237 */
235238fun IRequest.toCurlStringAsDartDevTools (gsonInstance : Gson ? = null): String {
236- // 创建一个列表来收集 cURL 命令的所有部分,方便最后用 `\` 连接
239+ // 检测操作系统
240+ val isWindows = System .getProperty(" os.name" ).lowercase().contains(" win" )
241+
242+ // 根据操作系统选择引号和转义方式
243+ val quote = if (isWindows) " \" " else " '"
244+ val lineContinuation = if (isWindows) " `" else " \\ "
245+
246+ // 转义函数:根据操作系统选择不同的转义策略
247+ fun escapeForShell (text : String ): String {
248+ return if (isWindows) {
249+ // Windows: 转义双引号和反斜杠
250+ text.replace(" \\ " , " \\\\ " ).replace(" \" " , " \\\" " )
251+ } else {
252+ // Unix: 转义单引号
253+ text.replace(" '" , " '\\ ''" )
254+ }
255+ }
256+
257+ // 创建一个列表来收集 cURL 命令的所有部分
237258 val commandParts = mutableListOf<String >()
238259
239260 // 1. HTTP 方法 (--request)
240- // Dart DevTools 总是显式包含方法
241261 httpMethod?.let {
242262 commandParts.add(" --request ${it.uppercase()} " )
243263 }
@@ -259,17 +279,15 @@ fun IRequest.toCurlStringAsDartDevTools(gsonInstance: Gson? = null): String {
259279 }
260280 }
261281 }
262- // 对 URL 中的特殊字符(主要是单引号)进行转义
263- commandParts.add(" '${finalUrl.replace(" '" , " '\\ ''" )} '" )
282+ commandParts.add(" $quote${escapeForShell(finalUrl)}$quote " )
264283
265284 // 3. 添加请求头 (--header)
266- // 过滤掉 cURL 会自动处理或通过标志管理的头
267285 val headersToFilter = setOf (" host" , " accept-encoding" , " content-length" )
268286 httpRequestHeaders
269287 .filterKeys { key -> ! headersToFilter.contains(key.lowercase()) }
270288 .forEach { (key, value) ->
271- val escapedValue = value.toString().replace( " ' " , " ' \\ '' " )
272- commandParts.add(" --header ' $ key : $escapedValue ' " )
289+ val escapedValue = escapeForShell( value.toString())
290+ commandParts.add(" --header $quote$ key : $escapedValue$quote " )
273291 }
274292
275293 // 4. 智能处理请求体 (--data-raw, --data)
@@ -284,28 +302,30 @@ fun IRequest.toCurlStringAsDartDevTools(gsonInstance: Gson? = null): String {
284302 is String -> body
285303 else -> (gsonInstance ? : Gson ()).toJson(body)
286304 }
287- val escapedBody = jsonBody.replace( " ' " , " ' \\ '' " )
288- " --data-raw ' $ escapedBody' "
305+ val escapedBody = escapeForShell(jsonBody )
306+ " --data-raw $quote$ escapedBody$quote "
289307 }
308+
290309 contentType.contains(" application/x-www-form-urlencoded" ) && body is Map <* , * > -> {
291310 val formData = body.map { (key, value) ->
292311 val encodedKey = URLEncoder .encode(key.toString(), " UTF-8" )
293312 val encodedValue = URLEncoder .encode(value?.toString() ? : " " , " UTF-8" )
294313 " $encodedKey =$encodedValue "
295314 }.joinToString(" &" )
296- " --data ' $ formData' "
315+ " --data $quote$ formData$quote "
297316 }
317+
298318 else -> {
299- val escapedBody = body.toString().replace( " ' " , " ' \\ '' " )
300- " --data ' $ escapedBody' "
319+ val escapedBody = escapeForShell( body.toString())
320+ " --data $quote$ escapedBody$quote "
301321 }
302322 }
303323 commandParts.add(bodyPart)
304324 }
305325
306326 // 5. 组合所有部分
307- // `curl --location --compressed` 是固定的前缀
308- // 使用 `joinToString` 来优雅地处理 `\` 和换行,确保最后一行没有 `\`
309- return " curl --location --compressed " + commandParts.joinToString(separator = " \\ \n " )
327+ // 使用适合当前操作系统的续行符
328+ return " curl --location --compressed $lineContinuation \n " +
329+ commandParts.joinToString(separator = " $lineContinuation \n " )
310330}
311331
0 commit comments