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