@@ -830,6 +830,16 @@ impl UserData for CandyReq {
830830 lua. create_string ( & digest. 0 ) . map ( mlua:: Value :: String )
831831 } ) ;
832832
833+ // sha1_bin(str): 计算字符串的 SHA-1 摘要
834+ // 返回原始二进制形式 (20 字节)
835+ methods. add_method_mut ( "sha1_bin" , |lua, _this, str : mlua:: BorrowedStr | {
836+ use sha1:: { Digest , Sha1 } ;
837+ let mut hasher = Sha1 :: new ( ) ;
838+ hasher. update ( str. as_bytes ( ) ) ;
839+ let result = hasher. finalize ( ) ;
840+ lua. create_string ( & result) . map ( mlua:: Value :: String )
841+ } ) ;
842+
833843 // log(log_level, ...): 记录日志消息
834844 // 将参数连接并记录到错误日志中,带有指定的日志级别
835845 methods. add_method_mut ( "log" , |_, _this, args : mlua:: MultiValue | {
@@ -1351,6 +1361,93 @@ impl UserData for RequestContext {
13511361 } ) ?;
13521362 Ok ( mlua:: Value :: Function ( update_time_func) )
13531363 }
1364+ "localtime" => {
1365+ // localtime(): 返回本地时间字符串 (格式: yyyy-mm-dd hh:mm:ss)
1366+ let localtime_func = lua. create_function ( |lua, ( ) | {
1367+ let now = chrono:: Local :: now ( ) ;
1368+ let formatted = now. format ( "%Y-%m-%d %H:%M:%S" ) . to_string ( ) ;
1369+ lua. create_string ( & formatted) . map ( mlua:: Value :: String )
1370+ } ) ?;
1371+ Ok ( mlua:: Value :: Function ( localtime_func) )
1372+ }
1373+ "utctime" => {
1374+ // utctime(): 返回 UTC 时间字符串 (格式: yyyy-mm-dd hh:mm:ss)
1375+ let utctime_func = lua. create_function ( |lua, ( ) | {
1376+ let now = chrono:: Utc :: now ( ) ;
1377+ let formatted = now. format ( "%Y-%m-%d %H:%M:%S" ) . to_string ( ) ;
1378+ lua. create_string ( & formatted) . map ( mlua:: Value :: String )
1379+ } ) ?;
1380+ Ok ( mlua:: Value :: Function ( utctime_func) )
1381+ }
1382+ "cookie_time" => {
1383+ // cookie_time(sec): 格式化时间戳为 cookie 过期时间格式
1384+ // 格式: "Thu, 18-Nov-10 11:27:35 GMT"
1385+ let cookie_time_func =
1386+ lua. create_function ( |lua, sec : i64 | {
1387+ use chrono:: { TimeZone , Utc } ;
1388+ match Utc . timestamp_opt ( sec, 0 ) {
1389+ chrono:: LocalResult :: Single ( dt) => {
1390+ // Cookie 格式: "Thu, 18-Nov-10 11:27:35 GMT"
1391+ let formatted = dt. format ( "%a, %d-%b-%y %H:%M:%S GMT" ) . to_string ( ) ;
1392+ lua. create_string ( & formatted) . map ( mlua:: Value :: String )
1393+ }
1394+ _ => Ok ( mlua:: Value :: Nil ) ,
1395+ }
1396+ } ) ?;
1397+ Ok ( mlua:: Value :: Function ( cookie_time_func) )
1398+ }
1399+ "http_time" => {
1400+ // http_time(sec): 格式化时间戳为 HTTP 头时间格式
1401+ // 格式: "Thu, 18 Nov 2010 11:27:35 GMT"
1402+ let http_time_func =
1403+ lua. create_function ( |lua, sec : i64 | {
1404+ use chrono:: { TimeZone , Utc } ;
1405+ match Utc . timestamp_opt ( sec, 0 ) {
1406+ chrono:: LocalResult :: Single ( dt) => {
1407+ // HTTP 格式: "Thu, 18 Nov 2010 11:27:35 GMT"
1408+ let formatted = dt. format ( "%a, %d %b %Y %H:%M:%S GMT" ) . to_string ( ) ;
1409+ lua. create_string ( & formatted) . map ( mlua:: Value :: String )
1410+ }
1411+ _ => Ok ( mlua:: Value :: Nil ) ,
1412+ }
1413+ } ) ?;
1414+ Ok ( mlua:: Value :: Function ( http_time_func) )
1415+ }
1416+ "parse_http_time" => {
1417+ // parse_http_time(str): 解析 HTTP 时间字符串为时间戳
1418+ // 支持多种格式: RFC1123, RFC850, asctime
1419+ let parse_http_time_func =
1420+ lua. create_function ( |lua, time_str : mlua:: String | {
1421+ let s = time_str. to_str ( ) ?;
1422+ let s: & str = & s;
1423+
1424+ // 尝试多种 HTTP 日期格式
1425+ // RFC1123: "Thu, 18 Nov 2010 11:27:35 GMT"
1426+ if let Ok ( dt) = chrono:: DateTime :: parse_from_rfc2822 ( s) {
1427+ return lua. pack ( dt. timestamp ( ) ) ;
1428+ }
1429+
1430+ // RFC850: "Thursday, 18-Nov-10 11:27:35 GMT"
1431+ // 格式: "%A, %d-%b-%y %H:%M:%S GMT"
1432+ if let Ok ( dt) = chrono:: DateTime :: parse_from_str (
1433+ s,
1434+ "%A, %d-%b-%y %H:%M:%S GMT" ,
1435+ ) {
1436+ return lua. pack ( dt. timestamp ( ) ) ;
1437+ }
1438+
1439+ // 尝试 asctime 格式: "Thu Nov 18 11:27:35 2010"
1440+ if let Ok ( dt) =
1441+ chrono:: DateTime :: parse_from_str ( s, "%a %b %d %H:%M:%S %Y" )
1442+ {
1443+ return lua. pack ( dt. timestamp ( ) ) ;
1444+ }
1445+
1446+ // 所有格式都解析失败
1447+ Ok ( mlua:: Value :: Nil )
1448+ } ) ?;
1449+ Ok ( mlua:: Value :: Function ( parse_http_time_func) )
1450+ }
13541451 // HTTP 方法常量
13551452 "HTTP_GET" => lua. pack ( HTTP_GET ) ,
13561453 "HTTP_HEAD" => lua. pack ( HTTP_HEAD ) ,
@@ -2200,6 +2297,92 @@ mod tests {
22002297 }
22012298 }
22022299
2300+ // SHA1 tests
2301+ mod sha1_tests {
2302+ use sha1:: { Digest , Sha1 } ;
2303+
2304+ fn compute_sha1 ( data : & [ u8 ] ) -> [ u8 ; 20 ] {
2305+ let mut hasher = Sha1 :: new ( ) ;
2306+ hasher. update ( data) ;
2307+ hasher. finalize ( ) . into ( )
2308+ }
2309+
2310+ fn to_hex ( bytes : & [ u8 ] ) -> String {
2311+ bytes. iter ( ) . map ( |b| format ! ( "{:02x}" , b) ) . collect ( )
2312+ }
2313+
2314+ #[ test]
2315+ fn test_sha1_bin_hello ( ) {
2316+ // SHA1("hello") = aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
2317+ let digest = compute_sha1 ( b"hello" ) ;
2318+ let hex = to_hex ( & digest) ;
2319+ assert_eq ! ( hex, "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" ) ;
2320+ }
2321+
2322+ #[ test]
2323+ fn test_sha1_bin_empty ( ) {
2324+ // SHA1("") = da39a3ee5e6b4b0d3255bfef95601890afd80709
2325+ let digest = compute_sha1 ( b"" ) ;
2326+ let hex = to_hex ( & digest) ;
2327+ assert_eq ! ( hex, "da39a3ee5e6b4b0d3255bfef95601890afd80709" ) ;
2328+ }
2329+
2330+ #[ test]
2331+ fn test_sha1_bin_output_length ( ) {
2332+ let digest = compute_sha1 ( b"test data" ) ;
2333+ assert_eq ! ( digest. len( ) , 20 ) ; // SHA1 always produces 20 bytes
2334+ }
2335+
2336+ #[ test]
2337+ fn test_sha1_bin_known_values ( ) {
2338+ // Test against known SHA1 values
2339+ assert_eq ! (
2340+ to_hex( & compute_sha1( b"" ) ) ,
2341+ "da39a3ee5e6b4b0d3255bfef95601890afd80709"
2342+ ) ;
2343+ assert_eq ! (
2344+ to_hex( & compute_sha1( b"a" ) ) ,
2345+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"
2346+ ) ;
2347+ assert_eq ! (
2348+ to_hex( & compute_sha1( b"abc" ) ) ,
2349+ "a9993e364706816aba3e25717850c26c9cd0d89d"
2350+ ) ;
2351+ assert_eq ! (
2352+ to_hex( & compute_sha1( b"message digest" ) ) ,
2353+ "c12252ceda8be8994d5fa0290a47231c1d16aae3"
2354+ ) ;
2355+ }
2356+
2357+ #[ test]
2358+ fn test_sha1_bin_consistency ( ) {
2359+ let data = b"consistent data" ;
2360+ let digest1 = compute_sha1 ( data) ;
2361+ let digest2 = compute_sha1 ( data) ;
2362+ assert_eq ! ( digest1, digest2) ;
2363+ }
2364+
2365+ #[ test]
2366+ fn test_sha1_bin_different_inputs ( ) {
2367+ let digest1 = compute_sha1 ( b"input1" ) ;
2368+ let digest2 = compute_sha1 ( b"input2" ) ;
2369+ assert_ne ! ( digest1, digest2) ;
2370+ }
2371+
2372+ #[ test]
2373+ fn test_sha1_bin_unicode ( ) {
2374+ let digest = compute_sha1 ( "中文测试" . as_bytes ( ) ) ;
2375+ assert_eq ! ( digest. len( ) , 20 ) ;
2376+ }
2377+
2378+ #[ test]
2379+ fn test_sha1_bin_long_string ( ) {
2380+ let long_str = "a" . repeat ( 1000 ) ;
2381+ let digest = compute_sha1 ( long_str. as_bytes ( ) ) ;
2382+ assert_eq ! ( digest. len( ) , 20 ) ;
2383+ }
2384+ }
2385+
22032386 // Edge cases
22042387 mod edge_cases {
22052388 use super :: * ;
0 commit comments