Function bodies 699 total
check_node_subscriptions function · rust · L971-L993 (23 LOC)crates/bubbaloop/src/cli/doctor.rs
async fn check_node_subscriptions(session: &Session) -> Vec<DiagnosticResult> {
let mut results = Vec::new();
// Try to subscribe to daemon nodes topic
match session.declare_subscriber("bubbaloop/daemon/nodes").await {
Ok(_subscriber) => {
results.push(DiagnosticResult::pass(
"Node subscription",
"can subscribe to bubbaloop/daemon/nodes",
));
}
Err(e) => {
results.push(DiagnosticResult::fail(
"Node subscription",
&format!("failed: {}", e),
"Check Zenoh router configuration",
));
}
}
results
}check_security function · rust · L996-L1025 (30 LOC)crates/bubbaloop/src/cli/doctor.rs
async fn check_security() -> Vec<DiagnosticResult> {
let mut results = Vec::new();
let home = match dirs::home_dir() {
Some(h) => h,
None => return results,
};
let bubbaloop_dir = home.join(".bubbaloop");
// Check 1: TLS enabled/disabled
results.push(check_tls_status(&bubbaloop_dir));
// Check 2: ACL configuration present
results.push(check_acl_status(&bubbaloop_dir));
// Check 3: Scouting disabled in zenoh config
results.push(check_scouting_disabled(&bubbaloop_dir));
// Check 4: Localhost-only binding
results.push(check_localhost_binding(&bubbaloop_dir));
// Check 5: Python node sandbox check
results.extend(check_python_sandbox());
// Check 6: Node name validation
results.extend(check_node_names(&bubbaloop_dir));
results
}check_tls_status function · rust · L1028-L1047 (20 LOC)crates/bubbaloop/src/cli/doctor.rs
fn check_tls_status(bubbaloop_dir: &std::path::Path) -> DiagnosticResult {
let zenoh_config = bubbaloop_dir.join("zenoh/zenohd.json5");
if let Ok(content) = std::fs::read_to_string(&zenoh_config) {
if content.contains("tls") && content.contains("server_certificate") {
DiagnosticResult::pass("TLS config", "TLS is configured")
} else {
DiagnosticResult::fail(
"TLS config",
"TLS not configured (plaintext transport)",
"Run: bubbaloop init-tls",
)
}
} else {
DiagnosticResult::fail(
"TLS config",
"Zenoh config not found",
"Run: bubbaloop doctor --fix",
)
}
}check_acl_status function · rust · L1050-L1069 (20 LOC)crates/bubbaloop/src/cli/doctor.rs
fn check_acl_status(bubbaloop_dir: &std::path::Path) -> DiagnosticResult {
let zenoh_config = bubbaloop_dir.join("zenoh/zenohd.json5");
if let Ok(content) = std::fs::read_to_string(&zenoh_config) {
if content.contains("access_control") {
DiagnosticResult::pass("ACL config", "access control rules present")
} else {
DiagnosticResult::fail(
"ACL config",
"no access control rules configured",
"See: configs/zenoh/acl-example.json5",
)
}
} else {
DiagnosticResult::fail(
"ACL config",
"Zenoh config not found",
"Run: bubbaloop doctor --fix",
)
}
}check_scouting_disabled function · rust · L1072-L1095 (24 LOC)crates/bubbaloop/src/cli/doctor.rs
fn check_scouting_disabled(bubbaloop_dir: &std::path::Path) -> DiagnosticResult {
let zenoh_config = bubbaloop_dir.join("zenoh/zenohd.json5");
if let Ok(content) = std::fs::read_to_string(&zenoh_config) {
// Check for multicast disabled
let multicast_disabled = content.contains("multicast")
&& content.contains("enabled")
&& content.contains("false");
if multicast_disabled {
DiagnosticResult::pass("Scouting", "multicast scouting disabled")
} else {
DiagnosticResult::fail(
"Scouting",
"multicast scouting may be enabled (security risk on shared networks)",
"Add scouting.multicast.enabled: false to zenohd.json5",
)
}
} else {
DiagnosticResult::fail(
"Scouting",
"Zenoh config not found",
"Run: bubbaloop doctor --fix",
)
}
}check_localhost_binding function · rust · L1098-L1118 (21 LOC)crates/bubbaloop/src/cli/doctor.rs
fn check_localhost_binding(bubbaloop_dir: &std::path::Path) -> DiagnosticResult {
let zenoh_config = bubbaloop_dir.join("zenoh/zenohd.json5");
if let Ok(content) = std::fs::read_to_string(&zenoh_config) {
// Check for 0.0.0.0 binding (allows remote connections)
if content.contains("0.0.0.0") {
DiagnosticResult::fail(
"Localhost binding",
"Zenoh binds to 0.0.0.0 (all interfaces)",
"Use 127.0.0.1 for local-only or enable TLS for remote access",
)
} else {
DiagnosticResult::pass("Localhost binding", "not binding to all interfaces")
}
} else {
DiagnosticResult::fail(
"Localhost binding",
"Zenoh config not found",
"Run: bubbaloop doctor --fix",
)
}
}check_python_sandbox function · rust · L1121-L1170 (50 LOC)crates/bubbaloop/src/cli/doctor.rs
fn check_python_sandbox() -> Vec<DiagnosticResult> {
let mut results = Vec::new();
let systemd_dir = dirs::home_dir()
.map(|h| h.join(".config/systemd/user"))
.unwrap_or_default();
if !systemd_dir.exists() {
return results;
}
let entries = match std::fs::read_dir(&systemd_dir) {
Ok(e) => e,
Err(_) => return results,
};
for entry in entries.flatten() {
let path = entry.path();
let name = entry.file_name().to_string_lossy().to_string();
if !name.starts_with("bubbaloop-") || !name.ends_with(".service") {
continue;
}
if let Ok(content) = std::fs::read_to_string(&path) {
// Only check Python nodes (they have PYTHONUNBUFFERED)
if !content.contains("PYTHONUNBUFFERED") {
continue;
}
let has_sandbox = content.contains("ProtectHome=read-only")
&& content.contains("MemoryMax=")
Repobility · code-quality intelligence · https://repobility.com
is_valid_node_name function · rust · L1173-L1179 (7 LOC)crates/bubbaloop/src/cli/doctor.rs
fn is_valid_node_name(name: &str) -> bool {
!name.is_empty()
&& name.len() <= 64
&& name
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-')
}check_node_names function · rust · L1182-L1211 (30 LOC)crates/bubbaloop/src/cli/doctor.rs
fn check_node_names(bubbaloop_dir: &std::path::Path) -> Vec<DiagnosticResult> {
let mut results = Vec::new();
let nodes_json = bubbaloop_dir.join("nodes.json");
if let Ok(content) = std::fs::read_to_string(&nodes_json) {
// Simple check: parse as JSON and validate each node name
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&content) {
if let Some(nodes) = parsed.get("nodes").and_then(|n| n.as_array()) {
for node in nodes {
if let Some(name) = node.get("name").and_then(|n| n.as_str()) {
if is_valid_node_name(name) {
results.push(DiagnosticResult::pass(
&format!("Node name ({})", name),
"valid",
));
} else {
results.push(DiagnosticResult::fail(
&format!("Node name ({})", query_with_timeout function · rust · L1212-L1244 (33 LOC)crates/bubbaloop/src/cli/doctor.rs
async fn query_with_timeout<T: for<'de> Deserialize<'de>>(
session: &Session,
key_expr: &str,
) -> Result<T> {
let replies = session
.get(key_expr)
.target(QueryTarget::BestMatching)
.timeout(Duration::from_secs(TIMEOUT_SECS))
.await
.map_err(|e| anyhow!("Zenoh query failed: {}", e))?;
let timeout_duration = Duration::from_secs(TIMEOUT_SECS);
match tokio::time::timeout(timeout_duration, replies.recv_async()).await {
Ok(Ok(reply)) => {
if let Ok(sample) = reply.result() {
let bytes = sample.payload().to_bytes();
let text = String::from_utf8_lossy(&bytes);
let result: T = serde_json::from_str(&text)?;
return Ok(result);
}
}
Ok(Err(e)) => return Err(anyhow!("Failed to receive reply: {}", e)),
Err(_) => { /* timeout - fall through to error below */ }
}
Err(anyhow!(
"No reply received for {} (ttest_diagnostic_result_pass function · rust · L1251-L1257 (7 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_diagnostic_result_pass() {
let result = DiagnosticResult::pass("test", "all good");
assert!(result.passed);
assert_eq!(result.check, "test");
assert_eq!(result.message, "all good");
assert!(result.fix.is_none());
}test_diagnostic_result_fail function · rust · L1260-L1266 (7 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_diagnostic_result_fail() {
let result = DiagnosticResult::fail("test", "something wrong", "do this");
assert!(!result.passed);
assert_eq!(result.check, "test");
assert_eq!(result.message, "something wrong");
assert_eq!(result.fix, Some("do this".to_string()));
}test_node_list_response_deserialization function · rust · L1276-L1284 (9 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_node_list_response_deserialization() {
let json = r#"{
"nodes": [],
"timestamp_ms": 1234567890
}"#;
let response: NodeListResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.nodes.len(), 0);
assert_eq!(response.timestamp_ms, 1234567890);
}test_node_state_deserialization function · rust · L1287-L1303 (17 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_node_state_deserialization() {
let json = r#"{
"name": "test-node",
"path": "/path",
"status": "running",
"installed": true,
"autostart_enabled": false,
"version": "1.0.0",
"description": "Test",
"node_type": "rust",
"is_built": true,
"build_output": []
}"#;
let node: NodeState = serde_json::from_str(json).unwrap();
assert_eq!(node.name, "test-node");
assert_eq!(node.status, "running");
}test_is_valid_node_name_accepts_valid function · rust · L1308-L1314 (7 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_is_valid_node_name_accepts_valid() {
assert!(is_valid_node_name("camera"));
assert!(is_valid_node_name("rtsp-camera"));
assert!(is_valid_node_name("my_node_123"));
assert!(is_valid_node_name("A"));
assert!(is_valid_node_name("a".repeat(64).as_str()));
}Repobility analyzer · published findings · https://repobility.com
test_is_valid_node_name_rejects_invalid function · rust · L1317-L1324 (8 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_is_valid_node_name_rejects_invalid() {
assert!(!is_valid_node_name(""));
assert!(!is_valid_node_name("bad node"));
assert!(!is_valid_node_name("bad/node"));
assert!(!is_valid_node_name("bad.node"));
assert!(!is_valid_node_name("bad\0node"));
assert!(!is_valid_node_name(&"a".repeat(65)));
}test_check_tls_status_with_tls_config function · rust · L1327-L1339 (13 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_tls_status_with_tls_config() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(
zenoh_dir.join("zenohd.json5"),
r#"{ transport: { link: { tls: { server_certificate: "/path/cert.pem" } } } }"#,
)
.unwrap();
let result = check_tls_status(dir.path());
assert!(result.passed, "Expected TLS check to pass");
}test_check_tls_status_without_tls function · rust · L1342-L1350 (9 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_tls_status_without_tls() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(zenoh_dir.join("zenohd.json5"), r#"{ mode: "router" }"#).unwrap();
let result = check_tls_status(dir.path());
assert!(!result.passed, "Expected TLS check to fail");
}test_check_scouting_disabled_passes function · rust · L1353-L1365 (13 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_scouting_disabled_passes() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(
zenoh_dir.join("zenohd.json5"),
r#"{ scouting: { multicast: { enabled: false } } }"#,
)
.unwrap();
let result = check_scouting_disabled(dir.path());
assert!(result.passed, "Expected scouting check to pass");
}test_check_scouting_disabled_fails_when_enabled function · rust · L1368-L1376 (9 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_scouting_disabled_fails_when_enabled() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(zenoh_dir.join("zenohd.json5"), r#"{ mode: "router" }"#).unwrap();
let result = check_scouting_disabled(dir.path());
assert!(!result.passed, "Expected scouting check to fail");
}test_check_localhost_binding_passes function · rust · L1379-L1391 (13 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_localhost_binding_passes() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(
zenoh_dir.join("zenohd.json5"),
r#"{ listen: { endpoints: ["tcp/127.0.0.1:7447"] } }"#,
)
.unwrap();
let result = check_localhost_binding(dir.path());
assert!(result.passed, "Expected localhost check to pass");
}test_check_localhost_binding_fails_wildcard function · rust · L1394-L1409 (16 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_localhost_binding_fails_wildcard() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(
zenoh_dir.join("zenohd.json5"),
r#"{ listen: { endpoints: ["tcp/0.0.0.0:7447"] } }"#,
)
.unwrap();
let result = check_localhost_binding(dir.path());
assert!(
!result.passed,
"Expected localhost check to fail for 0.0.0.0"
);
}test_check_acl_status_passes function · rust · L1412-L1424 (13 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_acl_status_passes() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(
zenoh_dir.join("zenohd.json5"),
r#"{ access_control: { enabled: true } }"#,
)
.unwrap();
let result = check_acl_status(dir.path());
assert!(result.passed, "Expected ACL check to pass");
}Open data scored by Repobility · https://repobility.com
test_check_acl_status_fails function · rust · L1427-L1435 (9 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_acl_status_fails() {
let dir = tempfile::tempdir().unwrap();
let zenoh_dir = dir.path().join("zenoh");
std::fs::create_dir_all(&zenoh_dir).unwrap();
std::fs::write(zenoh_dir.join("zenohd.json5"), r#"{ mode: "router" }"#).unwrap();
let result = check_acl_status(dir.path());
assert!(!result.passed, "Expected ACL check to fail");
}test_check_node_names_valid function · rust · L1438-L1449 (12 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_node_names_valid() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("nodes.json"),
r#"{"nodes": [{"name": "camera"}, {"name": "rtsp-cam"}]}"#,
)
.unwrap();
let results = check_node_names(dir.path());
assert_eq!(results.len(), 2);
assert!(results.iter().all(|r| r.passed));
}test_check_node_names_invalid function · rust · L1452-L1464 (13 LOC)crates/bubbaloop/src/cli/doctor.rs
fn test_check_node_names_invalid() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("nodes.json"),
r#"{"nodes": [{"name": "good-node"}, {"name": "bad node!"}]}"#,
)
.unwrap();
let results = check_node_names(dir.path());
assert_eq!(results.len(), 2);
assert!(results[0].passed);
assert!(!results[1].passed);
}parse_launch_file function · rust · L83-L91 (9 LOC)crates/bubbaloop/src/cli/launch.rs
fn parse_launch_file(content: &str) -> Result<LaunchFile> {
let launch: LaunchFile = serde_yaml::from_str(content)?;
if launch.name.is_empty() {
return Err(LaunchError::Instance(
"launch file must have a non-empty 'name' field".to_string(),
));
}
Ok(launch)
}write_config function · rust · L94-L104 (11 LOC)crates/bubbaloop/src/cli/launch.rs
fn write_config(
configs_dir: &std::path::Path,
name: &str,
config: &serde_yaml::Value,
) -> Result<PathBuf> {
std::fs::create_dir_all(configs_dir)?;
let dest = configs_dir.join(format!("{}.yaml", name));
let yaml = serde_yaml::to_string(config)?;
std::fs::write(&dest, &yaml)?;
Ok(dest)
}default_configs_dir function · rust · L105-L111 (7 LOC)crates/bubbaloop/src/cli/launch.rs
fn default_configs_dir() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".bubbaloop")
.join("configs")
}run function · rust · L114-L191 (78 LOC)crates/bubbaloop/src/cli/launch.rs
pub async fn run(self) -> Result<()> {
// 1. Read and parse the launch file
let file_path = std::path::Path::new(&self.file);
if !file_path.exists() {
return Err(LaunchError::FileNotFound(self.file.clone()));
}
let content = std::fs::read_to_string(file_path)?;
let launch = parse_launch_file(&content)?;
if self.dry_run {
println!("[DRY RUN] Instance: {}", launch.name);
println!(" Base node: {}", self.node);
println!(" Launch file: {}", self.file);
if launch.config.is_some() {
println!(
" Config: would write to ~/.bubbaloop/configs/{}.yaml",
launch.name
);
}
if self.build {
println!(" Would: build");
}
if self.install {
println!(" Would: install as systemd service");
}
if self.start {
resolve_node_path function · rust · L192-L237 (46 LOC)crates/bubbaloop/src/cli/launch.rs
async fn resolve_node_path(&self, node_name: &str) -> Result<String> {
let session = node::get_zenoh_session().await?;
let replies = session
.get("bubbaloop/daemon/api/nodes")
.target(zenoh::query::QueryTarget::BestMatching)
.timeout(std::time::Duration::from_secs(10))
.await
.map_err(|e| LaunchError::Node(node::NodeError::Zenoh(e.to_string())))?;
#[derive(Deserialize)]
struct NodeInfo {
name: String,
path: String,
}
#[derive(Deserialize)]
struct NodeListResponse {
nodes: Vec<NodeInfo>,
}
for reply in replies {
if let Ok(sample) = reply.into_result() {
let payload = sample.payload().to_bytes();
if let Ok(response) = serde_json::from_slice::<NodeListResponse>(&payload) {
for info in response.nodes {
if info.name == node_name {
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
register_instance function · rust · L238-L290 (53 LOC)crates/bubbaloop/src/cli/launch.rs
async fn register_instance(
&self,
node_path: &str,
name_override: Option<&str>,
config_path: Option<&str>,
) -> Result<()> {
let session = node::get_zenoh_session().await?;
let payload = serde_json::to_string(&serde_json::json!({
"command": "add",
"node_path": node_path,
"name": name_override,
"config": config_path,
}))?;
let replies = session
.get("bubbaloop/daemon/api/nodes/add")
.payload(payload)
.target(zenoh::query::QueryTarget::BestMatching)
.timeout(std::time::Duration::from_secs(30))
.await
.map_err(|e| LaunchError::Node(node::NodeError::Zenoh(e.to_string())))?;
let mut success = false;
for reply in replies {
if let Ok(sample) = reply.into_result() {
let data: serde_json::Value = serde_json::from_slice(&sample.payload().to_bytes())?;
test_parse_full_launch_file function · rust · L298-L310 (13 LOC)crates/bubbaloop/src/cli/launch.rs
fn test_parse_full_launch_file() {
let yaml = r#"
name: rtsp-camera-entrance
config:
name: entrance
publish_topic: camera/entrance/compressed
url: "rtsp://user:[email protected]:554/stream2"
latency: 200
"#;
let launch = parse_launch_file(yaml).unwrap();
assert_eq!(launch.name, "rtsp-camera-entrance");
assert!(launch.config.is_some());
}test_parse_minimal_launch_file function · rust · L313-L318 (6 LOC)crates/bubbaloop/src/cli/launch.rs
fn test_parse_minimal_launch_file() {
let yaml = "name: my-instance\n";
let launch = parse_launch_file(yaml).unwrap();
assert_eq!(launch.name, "my-instance");
assert!(launch.config.is_none());
}test_write_config_to_tempdir function · rust · L338-L351 (14 LOC)crates/bubbaloop/src/cli/launch.rs
fn test_write_config_to_tempdir() {
let dir = tempfile::tempdir().unwrap();
let config: serde_yaml::Value =
serde_yaml::from_str("name: terrace\npublish_topic: camera/terrace/compressed\n")
.unwrap();
let dest = write_config(dir.path(), "rtsp-camera-terrace", &config).unwrap();
assert!(dest.exists());
assert!(dest.to_string_lossy().ends_with("rtsp-camera-terrace.yaml"));
let content = std::fs::read_to_string(&dest).unwrap();
assert!(content.contains("terrace"));
assert!(content.contains("publish_topic"));
}test_write_config_nested_values function · rust · L354-L363 (10 LOC)crates/bubbaloop/src/cli/launch.rs
fn test_write_config_nested_values() {
let dir = tempfile::tempdir().unwrap();
let config: serde_yaml::Value =
serde_yaml::from_str("location:\n latitude: 41.39\n longitude: 2.17\n").unwrap();
let dest = write_config(dir.path(), "openmeteo", &config).unwrap();
let content = std::fs::read_to_string(&dest).unwrap();
assert!(content.contains("latitude"));
assert!(content.contains("41.39"));
}sources_path function · rust · L110-L116 (7 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn sources_path() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".bubbaloop")
.join("sources.json")
}load_sources function · rust · L117-L128 (12 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn load_sources() -> SourcesRegistry {
let path = sources_path();
if path.exists() {
fs::read_to_string(&path)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
} else {
SourcesRegistry::default()
}
}save_sources function · rust · L129-L138 (10 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn save_sources(registry: &SourcesRegistry) -> Result<()> {
let path = sources_path();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let json = serde_json::to_string_pretty(registry)?;
fs::write(path, json)?;
Ok(())
}Repobility · code-quality intelligence · https://repobility.com
find_source_mut function · rust · L139-L148 (10 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn find_source_mut<'a>(
registry: &'a mut SourcesRegistry,
name_or_path: &str,
) -> Option<&'a mut SourceEntry> {
registry
.sources
.iter_mut()
.find(|s| s.name == name_or_path || s.path == name_or_path)
}run function · rust · L151-L159 (9 LOC)crates/bubbaloop/src/cli/marketplace.rs
pub async fn run(self) -> Result<()> {
match self.action {
MarketplaceAction::List(args) => list_sources(args),
MarketplaceAction::Add(args) => add_source(args),
MarketplaceAction::Remove(args) => remove_source(args),
MarketplaceAction::Enable(args) => enable_source(args),
MarketplaceAction::Disable(args) => disable_source(args),
}
}list_sources function · rust · L161-L187 (27 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn list_sources(args: ListArgs) -> Result<()> {
let registry = load_sources();
if args.format == "json" {
println!("{}", serde_json::to_string_pretty(®istry.sources)?);
return Ok(());
}
if registry.sources.is_empty() {
println!("No marketplace sources configured.");
println!("Add one with: bubbaloop marketplace add <name> <path>");
return Ok(());
}
println!("{:<3} {:<20} {:<10} PATH", "ON", "NAME", "TYPE");
println!("{}", "-".repeat(70));
for source in ®istry.sources {
let enabled = if source.enabled { "yes" } else { "no" };
println!(
"{:<3} {:<20} {:<10} {}",
enabled, source.name, source.source_type, source.path
);
}
Ok(())
}add_source function · rust · L188-L208 (21 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn add_source(args: AddArgs) -> Result<()> {
let mut registry = load_sources();
if registry.sources.iter().any(|s| s.path == args.path) {
return Err(MarketplaceError::Other(
"Source with this path already exists".into(),
));
}
registry.sources.push(SourceEntry {
name: args.name.clone(),
path: args.path.clone(),
source_type: args.source_type,
enabled: true,
});
save_sources(®istry)?;
println!("Added marketplace source: {}", args.name);
Ok(())
}remove_source function · rust · L209-L241 (33 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn remove_source(args: RemoveArgs) -> Result<()> {
let mut registry = load_sources();
// Prevent removing builtin sources
if let Some(source) = registry
.sources
.iter()
.find(|s| s.name == args.name_or_path || s.path == args.name_or_path)
{
if source.source_type == OFFICIAL_SOURCE_TYPE {
return Err(MarketplaceError::Other(
"Cannot remove builtin source".into(),
));
}
}
let before = registry.sources.len();
registry
.sources
.retain(|s| s.name != args.name_or_path && s.path != args.name_or_path);
if registry.sources.len() == before {
return Err(MarketplaceError::Other(format!(
"Source '{}' not found",
args.name_or_path
)));
}
save_sources(®istry)?;
println!("Removed marketplace source: {}", args.name_or_path);
Ok(())
}update_source_enabled function · rust · L250-L261 (12 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn update_source_enabled(name_or_path: &str, enabled: bool, action: &str) -> Result<()> {
let mut registry = load_sources();
let source = find_source_mut(&mut registry, name_or_path)
.ok_or_else(|| MarketplaceError::Other(format!("Source '{}' not found", name_or_path)))?;
source.enabled = enabled;
save_sources(®istry)?;
println!("{} marketplace source: {}", action, name_or_path);
Ok(())
}test_sources_registry_round_trip function · rust · L268-L295 (28 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn test_sources_registry_round_trip() {
let registry = SourcesRegistry {
sources: vec![
SourceEntry {
name: "Official Nodes".into(),
path: "kornia/bubbaloop-nodes-official".into(),
source_type: "builtin".into(),
enabled: true,
},
SourceEntry {
name: "My Local".into(),
path: "/home/user/nodes".into(),
source_type: "local".into(),
enabled: false,
},
],
};
let json = serde_json::to_string_pretty(®istry).unwrap();
let parsed: SourcesRegistry = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.sources.len(), 2);
assert_eq!(parsed.sources[0].name, "Official Nodes");
assert_eq!(parsed.sources[0].source_type, "builtin");
assert!(parsed.sources[0].enabled);
assert_etest_serde_rename_type_field function · rust · L298-L307 (10 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn test_serde_rename_type_field() {
let json = r#"{"name":"test","path":"/path","type":"local","enabled":true}"#;
let entry: SourceEntry = serde_json::from_str(json).unwrap();
assert_eq!(entry.source_type, "local");
// Verify it serializes back with "type" key
let serialized = serde_json::to_string(&entry).unwrap();
assert!(serialized.contains("\"type\""));
assert!(!serialized.contains("\"source_type\""));
}Repobility analyzer · published findings · https://repobility.com
test_cannot_remove_builtin function · rust · L310-L347 (38 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn test_cannot_remove_builtin() {
let mut registry = SourcesRegistry {
sources: vec![SourceEntry {
name: "Official Nodes".into(),
path: "kornia/bubbaloop-nodes-official".into(),
source_type: "builtin".into(),
enabled: true,
}],
};
// Verify the source is found as builtin
let source = registry
.sources
.iter()
.find(|s| s.name == "Official Nodes")
.unwrap();
assert_eq!(source.source_type, OFFICIAL_SOURCE_TYPE);
// Simulate removal check
let is_builtin = registry
.sources
.iter()
.find(|s| s.name == "Official Nodes")
.map(|s| s.source_type == OFFICIAL_SOURCE_TYPE)
.unwrap_or(false);
assert!(is_builtin);
// Non-builtin should be removable
registry.sources.push(SourceEntry {
name: "Removable".into(),test_find_source_by_name_or_path function · rust · L350-L363 (14 LOC)crates/bubbaloop/src/cli/marketplace.rs
fn test_find_source_by_name_or_path() {
let mut registry = SourcesRegistry {
sources: vec![SourceEntry {
name: "My Nodes".into(),
path: "/home/user/nodes".into(),
source_type: "local".into(),
enabled: true,
}],
};
assert!(find_source_mut(&mut registry, "My Nodes").is_some());
assert!(find_source_mut(&mut registry, "/home/user/nodes").is_some());
assert!(find_source_mut(&mut registry, "nonexistent").is_none());
}copy_canonical_header_proto function · rust · L40-L56 (17 LOC)crates/bubbaloop/src/cli/node.rs
fn copy_canonical_header_proto(node_path: &Path) {
let protos_dir = node_path.join("protos");
if !protos_dir.exists() {
return;
}
let dest = protos_dir.join("header.proto");
if let Err(e) = std::fs::write(&dest, crate::HEADER_PROTO) {
log::warn!(
"Could not copy canonical header.proto to {}: {}",
dest.display(),
e
);
} else {
log::info!("Copied canonical header.proto to {}", dest.display());
}
}