|
1 | 1 | use std::{ |
2 | 2 | cell::RefCell, |
| 3 | + cmp::Ordering, |
3 | 4 | collections::HashMap, |
4 | 5 | ops::RangeInclusive, |
5 | 6 | rc::Rc, |
6 | | - sync::{Arc, RwLock}, |
| 7 | + sync::{Arc, RwLock, RwLockReadGuard}, |
7 | 8 | }; |
8 | 9 |
|
9 | | -use entrace_core::{EnValueRef, LogProviderError, MetadataRefContainer}; |
| 10 | +use anyhow::bail; |
| 11 | +use entrace_core::{EnValue, EnValueRef, LogProviderError, MetadataRefContainer}; |
10 | 12 | use memchr::memmem::Finder; |
11 | | -use mlua::{IntoLua, Lua, Table, Value}; |
| 13 | +use mlua::{ExternalError, IntoLua, Lua, Table, Value}; |
12 | 14 |
|
13 | 15 | use crate::{ |
14 | 16 | LevelRepr, TraceProvider, |
@@ -280,6 +282,178 @@ pub fn en_contains_anywhere( |
280 | 282 | } |
281 | 283 | } |
282 | 284 |
|
| 285 | +fn meta_matches( |
| 286 | + meta: &MetadataRefContainer, target: &str, comparator: Ordering, value: &EnValue, |
| 287 | +) -> anyhow::Result<bool> { |
| 288 | + fn string_eq(a: &str, value: &EnValue, comparator: std::cmp::Ordering) -> bool { |
| 289 | + match value { |
| 290 | + EnValue::String(b) => a.cmp(b) == comparator, |
| 291 | + _ => false, |
| 292 | + } |
| 293 | + } |
| 294 | + fn opt_string_eq(a: Option<&str>, value: &EnValue, comparator: Ordering) -> bool { |
| 295 | + let Some(a) = a else { return false }; |
| 296 | + match value { |
| 297 | + EnValue::String(b) => a.cmp(b) == comparator, |
| 298 | + _ => false, |
| 299 | + } |
| 300 | + } |
| 301 | + match target { |
| 302 | + "name" => Ok(string_eq(meta.name, value, comparator)), |
| 303 | + "target" => Ok(string_eq(meta.target, value, comparator)), |
| 304 | + "level" => { |
| 305 | + let asu8 = match value { |
| 306 | + EnValue::U64(x) => *x as u8, |
| 307 | + EnValue::I64(x) => *x as u8, |
| 308 | + _ => return Ok(false), |
| 309 | + }; |
| 310 | + Ok((meta.level as u8).cmp(&asu8) == comparator) |
| 311 | + } |
| 312 | + "module_path" => Ok(opt_string_eq(meta.module_path, value, comparator)), |
| 313 | + "file" => Ok(opt_string_eq(meta.file, value, comparator)), |
| 314 | + "line" => { |
| 315 | + let converted = match value { |
| 316 | + EnValue::Float(a) => *a as u32, |
| 317 | + EnValue::U64(a) => *a as u32, |
| 318 | + EnValue::I64(a) => *a as u32, |
| 319 | + _ => return Ok(false), |
| 320 | + }; |
| 321 | + let Some(line) = meta.line else { return Ok(false) }; |
| 322 | + Ok(line.cmp(&converted) == comparator) |
| 323 | + } |
| 324 | + x => bail!("Bad meta field {x}"), |
| 325 | + } |
| 326 | +} |
| 327 | + |
| 328 | +fn values_match(comparator: std::cmp::Ordering, span_value: &EnValueRef, value: &EnValue) -> bool { |
| 329 | + match value { |
| 330 | + EnValue::String(a) => match span_value { |
| 331 | + EnValueRef::String(b) => b.cmp(&a.as_str()) == comparator, |
| 332 | + _ => false, |
| 333 | + }, |
| 334 | + EnValue::Bool(a) => match span_value { |
| 335 | + EnValueRef::Bool(b) => b.cmp(a) == comparator, |
| 336 | + _ => false, |
| 337 | + }, |
| 338 | + EnValue::Float(a) => match span_value { |
| 339 | + EnValueRef::Float(b) => b.total_cmp(a) == comparator, |
| 340 | + _ => false, |
| 341 | + }, |
| 342 | + EnValue::U64(a) => { |
| 343 | + let span_value_converted = match span_value { |
| 344 | + EnValueRef::U64(x) => *x, |
| 345 | + EnValueRef::I64(x) => *x as u64, |
| 346 | + EnValueRef::U128(x) => *x as u64, |
| 347 | + EnValueRef::I128(x) => *x as u64, |
| 348 | + _ => return false, |
| 349 | + }; |
| 350 | + span_value_converted.cmp(a) == comparator |
| 351 | + } |
| 352 | + EnValue::I64(a) => { |
| 353 | + let span_value_converted = match span_value { |
| 354 | + EnValueRef::U64(x) => *x as i64, |
| 355 | + EnValueRef::I64(x) => *x, |
| 356 | + EnValueRef::U128(x) => *x as i64, |
| 357 | + EnValueRef::I128(x) => *x as i64, |
| 358 | + _ => return false, |
| 359 | + }; |
| 360 | + span_value_converted.cmp(a) == comparator |
| 361 | + } |
| 362 | + // we explicitly don't construct these |
| 363 | + EnValue::U128(_) => false, |
| 364 | + EnValue::I128(_) => false, |
| 365 | + // table->bytes is not handled for now |
| 366 | + EnValue::Bytes(_) => false, |
| 367 | + } |
| 368 | +} |
| 369 | + |
| 370 | +pub fn filter_inner( |
| 371 | + tcc: RwLockReadGuard<TraceProvider>, ids: impl Iterator<Item = u32>, target: &str, |
| 372 | + target_is_meta: bool, relation: Ordering, en_value: EnValue, |
| 373 | +) -> mlua::Result<Vec<u32>> { |
| 374 | + let mut returned = Vec::with_capacity(128); |
| 375 | + for id in ids { |
| 376 | + if target_is_meta { |
| 377 | + let meta = tcc.meta(id).unwrap(); |
| 378 | + let matches_here = |
| 379 | + meta_matches(&meta, target, relation, &en_value).map_err(|x| x.into_lua_err())?; |
| 380 | + if matches_here { |
| 381 | + returned.push(id); |
| 382 | + } |
| 383 | + } else { |
| 384 | + let attrs = tcc.attrs(id).unwrap(); |
| 385 | + let Some((_name, target_here)) = attrs.iter().find(|(name, _)| *name == target) else { |
| 386 | + continue; |
| 387 | + }; |
| 388 | + let matches_here = values_match(relation, target_here, &en_value); |
| 389 | + if matches_here { |
| 390 | + returned.push(id); |
| 391 | + } |
| 392 | + } |
| 393 | + } |
| 394 | + Ok(returned) |
| 395 | +} |
| 396 | + |
| 397 | +fn parse_filter_desc(desc: mlua::Table) -> mlua::Result<(String, bool, Ordering, EnValue)> { |
| 398 | + use anyhow::anyhow; |
| 399 | + let Ok(raw_target): mlua::Result<String> = desc.get("target") else { |
| 400 | + return Err(anyhow!("Filter target must be a string").into_lua_err()); |
| 401 | + }; |
| 402 | + let mut target: &str = raw_target.as_str(); |
| 403 | + let mut target_is_meta = false; |
| 404 | + if let Some(stripped) = raw_target.strip_prefix("meta.") { |
| 405 | + target = stripped; |
| 406 | + target_is_meta = true; |
| 407 | + } |
| 408 | + let Ok(rel): mlua::Result<String> = desc.get("relation") else { |
| 409 | + return Err(anyhow!("Filter relation must be a string").into_lua_err()); |
| 410 | + }; |
| 411 | + let relation = match rel.as_str() { |
| 412 | + "GT" => Ordering::Greater, |
| 413 | + "LT" => Ordering::Less, |
| 414 | + "EQ" => Ordering::Equal, |
| 415 | + x => return Err(anyhow::anyhow!("Bad filter relation {x}").into_lua_err()), |
| 416 | + }; |
| 417 | + |
| 418 | + let value: Value = desc.get("value").unwrap(); |
| 419 | + let en_value = match value { |
| 420 | + Value::Boolean(f) => EnValue::Bool(f), |
| 421 | + Value::Integer(k) => EnValue::I64(k), |
| 422 | + Value::Number(z) => EnValue::Float(z), |
| 423 | + Value::String(ref q) => EnValue::String(q.to_string_lossy()), |
| 424 | + x => { |
| 425 | + return Err(anyhow!("Cannot convert value {x:?} to EnValue").into_lua_err()); |
| 426 | + } |
| 427 | + }; |
| 428 | + Ok((target.into(), target_is_meta, relation, en_value)) |
| 429 | +} |
| 430 | + |
| 431 | +/// en_filter(list<id>, compare_desc) |
| 432 | +/// where compare_desc is a table: |
| 433 | +/// target: name of variable eg. "message" or "meta.filename" |
| 434 | +/// relation: a relation, one of "EQ", "LT", "GT" |
| 435 | +/// value: a constant to compare with |
| 436 | +/// Returns the ids of the spans matching the compare_desc. |
| 437 | +pub fn en_filter( |
| 438 | + trace_provider: Arc<RwLock<TraceProvider>>, |
| 439 | +) -> impl Fn(&Lua, (Vec<u32>, Table)) -> mlua::Result<Vec<u32>> { |
| 440 | + move |_lua: &Lua, (ids, desc): (Vec<u32>, Table)| { |
| 441 | + let tcc = trace_provider.read().unwrap(); |
| 442 | + let (target, target_is_meta, relation, en_value) = parse_filter_desc(desc)?; |
| 443 | + filter_inner(tcc, ids.iter().copied(), &target, target_is_meta, relation, en_value) |
| 444 | + } |
| 445 | +} |
| 446 | +/// Same as en_filter, but accets a range start and range end instead |
| 447 | +pub fn en_filter_range( |
| 448 | + trace_provider: Arc<RwLock<TraceProvider>>, |
| 449 | +) -> impl Fn(&Lua, (u32, u32, Table)) -> mlua::Result<Vec<u32>> { |
| 450 | + move |_lua: &Lua, (start, end, desc): (u32, u32, Table)| { |
| 451 | + let tcc = trace_provider.read().unwrap(); |
| 452 | + let (target, target_is_meta, relation, en_value) = parse_filter_desc(desc)?; |
| 453 | + filter_inner(tcc, start..=end, &target, target_is_meta, relation, en_value) |
| 454 | + } |
| 455 | +} |
| 456 | + |
283 | 457 | pub fn setup_lua( |
284 | 458 | lua: &mut Lua, range: RangeInclusive<u32>, trace: Arc<RwLock<TraceProvider>>, |
285 | 459 | finder_cache: Rc<RefCell<HashMap<String, Finder<'static>>>>, |
@@ -314,6 +488,8 @@ pub fn setup_lua( |
314 | 488 | "en_contains_anywhere", |
315 | 489 | lua.create_function(en_contains_anywhere(trace.clone(), fc))?, |
316 | 490 | )?; |
| 491 | + globals.set("en_filter", lua.create_function(en_filter(trace.clone()))?)?; |
| 492 | + globals.set("en_filter_range", lua.create_function(en_filter_range(trace.clone()))?)?; |
317 | 493 |
|
318 | 494 | Ok(()) |
319 | 495 | } |
0 commit comments