1use crate::service_mesh::{
331 RiServiceEndpoint, RiServiceMesh, RiServiceMeshConfig,
332 RiServiceDiscovery, RiServiceInstance,
333 RiHealthChecker, RiHealthStatus,
334 RiTrafficManager, RiTrafficRoute,
335};
336
337
338c_wrapper!(CRiServiceMesh, RiServiceMesh);
339c_wrapper!(CRiServiceMeshConfig, RiServiceMeshConfig);
340c_wrapper!(CRiServiceEndpoint, RiServiceEndpoint);
341
342c_constructor!(
344 ri_service_mesh_config_new,
345 CRiServiceMeshConfig,
346 RiServiceMeshConfig,
347 RiServiceMeshConfig::default()
348);
349c_destructor!(ri_service_mesh_config_free, CRiServiceMeshConfig);
350
351#[no_mangle]
353pub extern "C" fn ri_service_mesh_new(config: *mut CRiServiceMeshConfig) -> *mut CRiServiceMesh {
354 if config.is_null() {
355 return std::ptr::null_mut();
356 }
357 unsafe {
358 let config = (*config).inner.clone();
359 match RiServiceMesh::new(config) {
360 Ok(mesh) => Box::into_raw(Box::new(CRiServiceMesh::new(mesh))),
361 Err(_) => std::ptr::null_mut(),
362 }
363 }
364}
365c_destructor!(ri_service_mesh_free, CRiServiceMesh);
366
367#[no_mangle]
369pub extern "C" fn ri_service_endpoint_new(
370 service_name: *const std::ffi::c_char,
371 endpoint: *const std::ffi::c_char,
372 weight: u32,
373) -> *mut CRiServiceEndpoint {
374 if service_name.is_null() || endpoint.is_null() {
375 return std::ptr::null_mut();
376 }
377 unsafe {
378 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
379 Ok(s) => s.to_string(),
380 Err(_) => return std::ptr::null_mut(),
381 };
382 let endpoint_str = match std::ffi::CStr::from_ptr(endpoint).to_str() {
383 Ok(s) => s.to_string(),
384 Err(_) => return std::ptr::null_mut(),
385 };
386 let ep = RiServiceEndpoint {
387 service_name: service_name_str,
388 endpoint: endpoint_str,
389 weight,
390 metadata: std::collections::HashMap::default(),
391 health_status: crate::service_mesh::RiServiceHealthStatus::Unknown,
392 last_health_check: std::time::SystemTime::now(),
393 };
394 Box::into_raw(Box::new(CRiServiceEndpoint::new(ep)))
395 }
396}
397c_destructor!(ri_service_endpoint_free, CRiServiceEndpoint);
398
399c_string_getter!(
401 ri_service_endpoint_get_service_name,
402 CRiServiceEndpoint,
403 |inner: &RiServiceEndpoint| inner.service_name.clone()
404);
405c_string_getter!(
406 ri_service_endpoint_get_endpoint,
407 CRiServiceEndpoint,
408 |inner: &RiServiceEndpoint| inner.endpoint.clone()
409);
410
411#[no_mangle]
412pub extern "C" fn ri_service_endpoint_get_weight(obj: *mut CRiServiceEndpoint) -> u32 {
413 if obj.is_null() {
414 return 0;
415 }
416 unsafe { (*obj).inner.weight }
417}
418
419#[no_mangle]
420pub extern "C" fn ri_service_endpoint_get_health_status(obj: *mut CRiServiceEndpoint) -> std::ffi::c_int {
421 if obj.is_null() {
422 return -1;
423 }
424 unsafe {
425 match (*obj).inner.health_status {
426 crate::service_mesh::RiServiceHealthStatus::Healthy => 0,
427 crate::service_mesh::RiServiceHealthStatus::Unhealthy => 1,
428 crate::service_mesh::RiServiceHealthStatus::Unknown => 2,
429 }
430 }
431}
432
433#[no_mangle]
435pub extern "C" fn ri_service_mesh_register(
436 mesh: *mut CRiServiceMesh,
437 endpoint: *mut CRiServiceEndpoint,
438) -> std::ffi::c_int {
439 if mesh.is_null() || endpoint.is_null() {
440 return -1;
441 }
442 let rt = match tokio::runtime::Runtime::new() {
443 Ok(rt) => rt,
444 Err(_) => return -2,
445 };
446 unsafe {
447 let ep = (*endpoint).inner.clone();
448 rt.block_on(async {
449 match (*mesh).inner.register_service(&ep.service_name, &ep.endpoint, ep.weight, None).await {
450 Ok(_) => 0,
451 Err(_) => -3,
452 }
453 })
454 }
455}
456
457#[no_mangle]
458pub extern "C" fn ri_service_mesh_deregister(
459 mesh: *mut CRiServiceMesh,
460 service_name: *const std::ffi::c_char,
461 endpoint: *const std::ffi::c_char,
462) -> std::ffi::c_int {
463 if mesh.is_null() || service_name.is_null() || endpoint.is_null() {
464 return -1;
465 }
466 let rt = match tokio::runtime::Runtime::new() {
467 Ok(rt) => rt,
468 Err(_) => return -2,
469 };
470 unsafe {
471 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
472 Ok(s) => s,
473 Err(_) => return -3,
474 };
475 let endpoint_str = match std::ffi::CStr::from_ptr(endpoint).to_str() {
476 Ok(s) => s,
477 Err(_) => return -3,
478 };
479 rt.block_on(async {
480 match (*mesh).inner.unregister_service(service_name_str, endpoint_str).await {
481 Ok(_) => 0,
482 Err(_) => -4,
483 }
484 })
485 }
486}
487
488#[no_mangle]
489pub extern "C" fn ri_service_mesh_discover(
490 mesh: *mut CRiServiceMesh,
491 service_name: *const std::ffi::c_char,
492 out_endpoints: *mut *mut CRiServiceEndpoint,
493 out_count: *mut usize,
494) -> std::ffi::c_int {
495 if mesh.is_null() || service_name.is_null() || out_endpoints.is_null() || out_count.is_null() {
496 return -1;
497 }
498 let rt = match tokio::runtime::Runtime::new() {
499 Ok(rt) => rt,
500 Err(_) => return -2,
501 };
502 unsafe {
503 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
504 Ok(s) => s,
505 Err(_) => return -3,
506 };
507 match rt.block_on(async { (*mesh).inner.discover_service(service_name_str).await }) {
508 Ok(endpoints) => {
509 let count = endpoints.len();
510 *out_count = count;
511 if count == 0 {
512 *out_endpoints = std::ptr::null_mut();
513 return 0;
514 }
515 let ptr = Box::into_raw(Box::new(Vec::with_capacity(count)));
516 (*ptr).extend(endpoints.into_iter().map(CRiServiceEndpoint::new));
517 *out_endpoints = ptr as *mut CRiServiceEndpoint;
518 0
519 }
520 Err(_) => -4,
521 }
522 }
523}
524
525#[no_mangle]
526pub extern "C" fn ri_service_mesh_get_healthy_count(
527 mesh: *mut CRiServiceMesh,
528 service_name: *const std::ffi::c_char,
529) -> u32 {
530 if mesh.is_null() || service_name.is_null() {
531 return 0;
532 }
533 let rt = match tokio::runtime::Runtime::new() {
534 Ok(rt) => rt,
535 Err(_) => return 0,
536 };
537 unsafe {
538 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
539 Ok(s) => s,
540 Err(_) => return 0,
541 };
542 match rt.block_on(async { (*mesh).inner.discover_service(service_name_str).await }) {
543 Ok(endpoints) => endpoints.len() as u32,
544 Err(_) => 0,
545 }
546 }
547}
548
549#[no_mangle]
550pub extern "C" fn ri_service_mesh_get_stats(
551 mesh: *mut CRiServiceMesh,
552 out_total_services: *mut usize,
553 out_total_endpoints: *mut usize,
554 out_healthy_endpoints: *mut usize,
555 out_unhealthy_endpoints: *mut usize,
556) -> std::ffi::c_int {
557 if mesh.is_null() {
558 return -1;
559 }
560 let rt = match tokio::runtime::Runtime::new() {
561 Ok(rt) => rt,
562 Err(_) => return -2,
563 };
564 unsafe {
565 let stats = rt.block_on(async { (*mesh).inner.get_stats().await });
566 if !out_total_services.is_null() {
567 *out_total_services = stats.total_services;
568 }
569 if !out_total_endpoints.is_null() {
570 *out_total_endpoints = stats.total_endpoints;
571 }
572 if !out_healthy_endpoints.is_null() {
573 *out_healthy_endpoints = stats.healthy_endpoints;
574 }
575 if !out_unhealthy_endpoints.is_null() {
576 *out_unhealthy_endpoints = stats.unhealthy_endpoints;
577 }
578 0
579 }
580}
581
582c_wrapper!(CRiServiceDiscovery, RiServiceDiscovery);
584
585#[no_mangle]
586pub extern "C" fn ri_service_discovery_new(enabled: bool) -> *mut CRiServiceDiscovery {
587 Box::into_raw(Box::new(CRiServiceDiscovery::new(RiServiceDiscovery::new(enabled))))
588}
589c_destructor!(ri_service_discovery_free, CRiServiceDiscovery);
590
591#[no_mangle]
592pub extern "C" fn ri_service_discovery_register(
593 discovery: *mut CRiServiceDiscovery,
594 service_name: *const std::ffi::c_char,
595 host: *const std::ffi::c_char,
596 port: u16,
597) -> *mut std::ffi::c_char {
598 if discovery.is_null() || service_name.is_null() || host.is_null() {
599 return std::ptr::null_mut();
600 }
601 let rt = match tokio::runtime::Runtime::new() {
602 Ok(rt) => rt,
603 Err(_) => return std::ptr::null_mut(),
604 };
605 unsafe {
606 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
607 Ok(s) => s,
608 Err(_) => return std::ptr::null_mut(),
609 };
610 let host_str = match std::ffi::CStr::from_ptr(host).to_str() {
611 Ok(s) => s,
612 Err(_) => return std::ptr::null_mut(),
613 };
614 match rt.block_on(async {
615 (*discovery).inner.register_service(service_name_str, host_str, port, std::collections::HashMap::default()).await
616 }) {
617 Ok(instance_id) => match std::ffi::CString::new(instance_id) {
618 Ok(c_str) => c_str.into_raw(),
619 Err(_) => std::ptr::null_mut(),
620 },
621 Err(_) => std::ptr::null_mut(),
622 }
623 }
624}
625
626#[no_mangle]
627pub extern "C" fn ri_service_discovery_deregister(
628 discovery: *mut CRiServiceDiscovery,
629 instance_id: *const std::ffi::c_char,
630) -> std::ffi::c_int {
631 if discovery.is_null() || instance_id.is_null() {
632 return -1;
633 }
634 let rt = match tokio::runtime::Runtime::new() {
635 Ok(rt) => rt,
636 Err(_) => return -2,
637 };
638 unsafe {
639 let instance_id_str = match std::ffi::CStr::from_ptr(instance_id).to_str() {
640 Ok(s) => s,
641 Err(_) => return -3,
642 };
643 match rt.block_on(async { (*discovery).inner.deregister_service(instance_id_str).await }) {
644 Ok(_) => 0,
645 Err(_) => -4,
646 }
647 }
648}
649
650#[no_mangle]
651pub extern "C" fn ri_service_discovery_discover(
652 discovery: *mut CRiServiceDiscovery,
653 service_name: *const std::ffi::c_char,
654 out_instances: *mut *mut CRiServiceInstance,
655 out_count: *mut usize,
656) -> std::ffi::c_int {
657 if discovery.is_null() || service_name.is_null() || out_instances.is_null() || out_count.is_null() {
658 return -1;
659 }
660 let rt = match tokio::runtime::Runtime::new() {
661 Ok(rt) => rt,
662 Err(_) => return -2,
663 };
664 unsafe {
665 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
666 Ok(s) => s,
667 Err(_) => return -3,
668 };
669 match rt.block_on(async { (*discovery).inner.discover_service(service_name_str).await }) {
670 Ok(instances) => {
671 let count = instances.len();
672 *out_count = count;
673 if count == 0 {
674 *out_instances = std::ptr::null_mut();
675 return 0;
676 }
677 let ptr = Box::into_raw(Box::new(Vec::with_capacity(count)));
678 (*ptr).extend(instances.into_iter().map(CRiServiceInstance::new));
679 *out_instances = ptr as *mut CRiServiceInstance;
680 0
681 }
682 Err(_) => -4,
683 }
684 }
685}
686
687c_wrapper!(CRiServiceInstance, RiServiceInstance);
689
690c_string_getter!(
691 ri_service_instance_get_id,
692 CRiServiceInstance,
693 |inner: &RiServiceInstance| inner.id.clone()
694);
695c_string_getter!(
696 ri_service_instance_get_service_name,
697 CRiServiceInstance,
698 |inner: &RiServiceInstance| inner.service_name.clone()
699);
700c_string_getter!(
701 ri_service_instance_get_host,
702 CRiServiceInstance,
703 |inner: &RiServiceInstance| inner.host.clone()
704);
705
706#[no_mangle]
707pub extern "C" fn ri_service_instance_get_port(obj: *mut CRiServiceInstance) -> u16 {
708 if obj.is_null() {
709 return 0;
710 }
711 unsafe { (*obj).inner.port }
712}
713
714#[no_mangle]
715pub extern "C" fn ri_service_instance_get_status(obj: *mut CRiServiceInstance) -> std::ffi::c_int {
716 if obj.is_null() {
717 return -1;
718 }
719 unsafe {
720 match (*obj).inner.status {
721 crate::service_mesh::RiServiceStatus::Starting => 0,
722 crate::service_mesh::RiServiceStatus::Running => 1,
723 crate::service_mesh::RiServiceStatus::Stopping => 2,
724 crate::service_mesh::RiServiceStatus::Stopped => 3,
725 crate::service_mesh::RiServiceStatus::Unhealthy => 4,
726 }
727 }
728}
729
730c_destructor!(ri_service_instance_free, CRiServiceInstance);
731
732c_wrapper!(CRiHealthChecker, RiHealthChecker);
734
735#[no_mangle]
736pub extern "C" fn ri_health_checker_new(check_interval_secs: u64) -> *mut CRiHealthChecker {
737 Box::into_raw(Box::new(CRiHealthChecker::new(RiHealthChecker::new(std::time::Duration::from_secs(check_interval_secs)))))
738}
739c_destructor!(ri_health_checker_free, CRiHealthChecker);
740
741#[no_mangle]
742pub extern "C" fn ri_health_checker_start(
743 checker: *mut CRiHealthChecker,
744 service_name: *const std::ffi::c_char,
745 endpoint: *const std::ffi::c_char,
746) -> std::ffi::c_int {
747 if checker.is_null() || service_name.is_null() || endpoint.is_null() {
748 return -1;
749 }
750 let rt = match tokio::runtime::Runtime::new() {
751 Ok(rt) => rt,
752 Err(_) => return -2,
753 };
754 unsafe {
755 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
756 Ok(s) => s,
757 Err(_) => return -3,
758 };
759 let endpoint_str = match std::ffi::CStr::from_ptr(endpoint).to_str() {
760 Ok(s) => s,
761 Err(_) => return -3,
762 };
763 match rt.block_on(async { (*checker).inner.start_health_check(service_name_str, endpoint_str).await }) {
764 Ok(_) => 0,
765 Err(_) => -4,
766 }
767 }
768}
769
770#[no_mangle]
771pub extern "C" fn ri_health_checker_stop(
772 checker: *mut CRiHealthChecker,
773 service_name: *const std::ffi::c_char,
774 endpoint: *const std::ffi::c_char,
775) -> std::ffi::c_int {
776 if checker.is_null() || service_name.is_null() || endpoint.is_null() {
777 return -1;
778 }
779 let rt = match tokio::runtime::Runtime::new() {
780 Ok(rt) => rt,
781 Err(_) => return -2,
782 };
783 unsafe {
784 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
785 Ok(s) => s,
786 Err(_) => return -3,
787 };
788 let endpoint_str = match std::ffi::CStr::from_ptr(endpoint).to_str() {
789 Ok(s) => s,
790 Err(_) => return -3,
791 };
792 match rt.block_on(async { (*checker).inner.stop_health_check(service_name_str, endpoint_str).await }) {
793 Ok(_) => 0,
794 Err(_) => -4,
795 }
796 }
797}
798
799#[no_mangle]
800pub extern "C" fn ri_health_checker_get_summary(
801 checker: *mut CRiHealthChecker,
802 service_name: *const std::ffi::c_char,
803 out_healthy: *mut bool,
804 out_success_rate: *mut f64,
805 out_avg_response_ms: *mut u64,
806) -> std::ffi::c_int {
807 if checker.is_null() || service_name.is_null() {
808 return -1;
809 }
810 let rt = match tokio::runtime::Runtime::new() {
811 Ok(rt) => rt,
812 Err(_) => return -2,
813 };
814 unsafe {
815 let service_name_str = match std::ffi::CStr::from_ptr(service_name).to_str() {
816 Ok(s) => s,
817 Err(_) => return -3,
818 };
819 match rt.block_on(async { (*checker).inner.get_service_health_summary(service_name_str).await }) {
820 Ok(summary) => {
821 if !out_healthy.is_null() {
822 *out_healthy = matches!(summary.overall_status, RiHealthStatus::Healthy);
823 }
824 if !out_success_rate.is_null() {
825 *out_success_rate = summary.success_rate;
826 }
827 if !out_avg_response_ms.is_null() {
828 *out_avg_response_ms = summary.average_response_time.as_millis() as u64;
829 }
830 0
831 }
832 Err(_) => -4,
833 }
834 }
835}
836
837c_wrapper!(CRiTrafficManager, RiTrafficManager);
839
840#[no_mangle]
841pub extern "C" fn ri_traffic_manager_new(enabled: bool) -> *mut CRiTrafficManager {
842 Box::into_raw(Box::new(CRiTrafficManager::new(RiTrafficManager::new(enabled))))
843}
844c_destructor!(ri_traffic_manager_free, CRiTrafficManager);
845
846#[no_mangle]
847pub extern "C" fn ri_traffic_manager_add_route(
848 manager: *mut CRiTrafficManager,
849 route: *mut CRiTrafficRoute,
850) -> std::ffi::c_int {
851 if manager.is_null() || route.is_null() {
852 return -1;
853 }
854 let rt = match tokio::runtime::Runtime::new() {
855 Ok(rt) => rt,
856 Err(_) => return -2,
857 };
858 unsafe {
859 match rt.block_on(async { (*manager).inner.add_traffic_route((*route).inner.clone()).await }) {
860 Ok(_) => 0,
861 Err(_) => -3,
862 }
863 }
864}
865
866#[no_mangle]
867pub extern "C" fn ri_traffic_manager_remove_route(
868 manager: *mut CRiTrafficManager,
869 source_service: *const std::ffi::c_char,
870 route_name: *const std::ffi::c_char,
871) -> std::ffi::c_int {
872 if manager.is_null() || source_service.is_null() || route_name.is_null() {
873 return -1;
874 }
875 let rt = match tokio::runtime::Runtime::new() {
876 Ok(rt) => rt,
877 Err(_) => return -2,
878 };
879 unsafe {
880 let source_service_str = match std::ffi::CStr::from_ptr(source_service).to_str() {
881 Ok(s) => s,
882 Err(_) => return -3,
883 };
884 let route_name_str = match std::ffi::CStr::from_ptr(route_name).to_str() {
885 Ok(s) => s,
886 Err(_) => return -3,
887 };
888 match rt.block_on(async { (*manager).inner.remove_traffic_route(source_service_str, route_name_str).await }) {
889 Ok(_) => 0,
890 Err(_) => -4,
891 }
892 }
893}
894
895#[no_mangle]
896pub extern "C" fn ri_traffic_manager_set_circuit_breaker(
897 manager: *mut CRiTrafficManager,
898 service: *const std::ffi::c_char,
899 consecutive_errors: u32,
900 max_ejection_percent: f64,
901) -> std::ffi::c_int {
902 if manager.is_null() || service.is_null() {
903 return -1;
904 }
905 let rt = match tokio::runtime::Runtime::new() {
906 Ok(rt) => rt,
907 Err(_) => return -2,
908 };
909 unsafe {
910 let service_str = match std::ffi::CStr::from_ptr(service).to_str() {
911 Ok(s) => s,
912 Err(_) => return -3,
913 };
914 let config = crate::service_mesh::traffic_management::RiCircuitBreakerConfig {
915 consecutive_errors,
916 interval: std::time::Duration::from_secs(10),
917 base_ejection_time: std::time::Duration::from_secs(30),
918 max_ejection_percent,
919 };
920 match rt.block_on(async { (*manager).inner.set_circuit_breaker_config(service_str, config).await }) {
921 Ok(_) => 0,
922 Err(_) => -4,
923 }
924 }
925}
926
927#[no_mangle]
928pub extern "C" fn ri_traffic_manager_set_rate_limit(
929 manager: *mut CRiTrafficManager,
930 service: *const std::ffi::c_char,
931 requests_per_second: u32,
932 burst_size: u32,
933) -> std::ffi::c_int {
934 if manager.is_null() || service.is_null() {
935 return -1;
936 }
937 let rt = match tokio::runtime::Runtime::new() {
938 Ok(rt) => rt,
939 Err(_) => return -2,
940 };
941 unsafe {
942 let service_str = match std::ffi::CStr::from_ptr(service).to_str() {
943 Ok(s) => s,
944 Err(_) => return -3,
945 };
946 let config = crate::service_mesh::traffic_management::RiRateLimitConfig {
947 requests_per_second,
948 burst_size,
949 window: std::time::Duration::from_secs(1),
950 };
951 match rt.block_on(async { (*manager).inner.set_rate_limit_config(service_str, config).await }) {
952 Ok(_) => 0,
953 Err(_) => -4,
954 }
955 }
956}
957
958c_wrapper!(CRiTrafficRoute, RiTrafficRoute);
960
961#[no_mangle]
962pub extern "C" fn ri_traffic_route_new(
963 name: *const std::ffi::c_char,
964 source_service: *const std::ffi::c_char,
965 destination_service: *const std::ffi::c_char,
966) -> *mut CRiTrafficRoute {
967 if name.is_null() || source_service.is_null() || destination_service.is_null() {
968 return std::ptr::null_mut();
969 }
970 unsafe {
971 let name_str = match std::ffi::CStr::from_ptr(name).to_str() {
972 Ok(s) => s.to_string(),
973 Err(_) => return std::ptr::null_mut(),
974 };
975 let source_str = match std::ffi::CStr::from_ptr(source_service).to_str() {
976 Ok(s) => s.to_string(),
977 Err(_) => return std::ptr::null_mut(),
978 };
979 let dest_str = match std::ffi::CStr::from_ptr(destination_service).to_str() {
980 Ok(s) => s.to_string(),
981 Err(_) => return std::ptr::null_mut(),
982 };
983 let route = RiTrafficRoute {
984 name: name_str,
985 source_service: source_str,
986 destination_service: dest_str,
987 match_criteria: crate::service_mesh::RiMatchCriteria {
988 path_prefix: None,
989 headers: std::collections::HashMap::default(),
990 method: None,
991 query_parameters: std::collections::HashMap::default(),
992 },
993 route_action: crate::service_mesh::RiRouteAction::Route(vec![]),
994 retry_policy: None,
995 timeout: None,
996 fault_injection: None,
997 };
998 Box::into_raw(Box::new(CRiTrafficRoute::new(route)))
999 }
1000}
1001c_destructor!(ri_traffic_route_free, CRiTrafficRoute);
1002
1003c_string_getter!(
1004 ri_traffic_route_get_name,
1005 CRiTrafficRoute,
1006 |inner: &RiTrafficRoute| inner.name.clone()
1007);
1008c_string_getter!(
1009 ri_traffic_route_get_source_service,
1010 CRiTrafficRoute,
1011 |inner: &RiTrafficRoute| inner.source_service.clone()
1012);
1013c_string_getter!(
1014 ri_traffic_route_get_destination_service,
1015 CRiTrafficRoute,
1016 |inner: &RiTrafficRoute| inner.destination_service.clone()
1017);