mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-28 17:52:50 +01:00
* fix hls close revision file at reader deadline Move the progressive revision reader onto its own task and feed the response stream through a bounded channel. At the deadline the watchdog aborts the reader, so the file handle and revision pin are released even if a stalled client never polls the stream again. Each open revision file now holds its own pin, so GC cannot retire a revision while a handle is open, including one held by an in-flight blocking read. * fix hls close revision file at reader deadline Move the progressive revision reader onto its own task and feed the response stream through a bounded channel. At the deadline the watchdog aborts the reader, so the file handle and revision pin are released even if a stalled client never polls the stream again. Each open revision file now holds its own pin, so GC cannot retire a revision while a handle is open, including one held by an in-flight blocking read. --------- Co-authored-by: euzu <euzu@proton.me>
80 lines
2.4 KiB
Rust
80 lines
2.4 KiB
Rust
#![allow(clippy::empty_docs)]
|
|
|
|
use crate::model::{FieldGetAccessor, FieldSetAccessor, PatternTemplate, PlaylistItem};
|
|
use log::{error, trace};
|
|
use std::{collections::HashMap, sync::Arc};
|
|
|
|
///
|
|
/// The `ValueAccessor` does not respect `match_as_ascii` when returning values.
|
|
/// This is intentional because assignments must not change the value content through normalization.
|
|
/// Normalization should only be applied during comparisons or when processing values with regular expressions.
|
|
///
|
|
pub struct ValueAccessor<'a> {
|
|
pub pli: &'a mut PlaylistItem,
|
|
pub virtual_items: Vec<(String, PlaylistItem)>,
|
|
pub match_as_ascii: bool,
|
|
pub changed_fields: Vec<String>,
|
|
}
|
|
|
|
impl ValueAccessor<'_> {
|
|
pub fn get(&self, field: &str) -> Option<Arc<str>> { self.pli.header.get_field(field) }
|
|
|
|
pub fn set(&mut self, field: &str, value: &str) {
|
|
if self.pli.header.set_field(field, value) {
|
|
if !self.changed_fields.iter().any(|changed| changed.eq_ignore_ascii_case(field)) {
|
|
self.changed_fields.push(field.to_string());
|
|
}
|
|
trace!("Property {field} set to {value}");
|
|
} else {
|
|
error!("Can't set unknown field {field} set to {value}");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct MapperScript {
|
|
pub expressions: Vec<Expression>,
|
|
pub statements: Vec<Statement>,
|
|
}
|
|
|
|
pub struct MapperContext<'a> {
|
|
expressions: &'a [Expression],
|
|
variables: HashMap<String, EvalResult>,
|
|
templates: Option<&'a [PatternTemplate]>,
|
|
}
|
|
|
|
macro_rules! extract_evaluated_arg_value {
|
|
($evaluated_args:expr, $index:expr) => {{
|
|
if $index >= $evaluated_args.len() {
|
|
None
|
|
} else {
|
|
let evaluated_arg = &$evaluated_args[$index];
|
|
match evaluated_arg {
|
|
Value(value) => Some(value),
|
|
Named(values) => values.first().map(|(_key, val)| val),
|
|
_ => None,
|
|
}
|
|
}
|
|
}};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
mod ast;
|
|
mod builtins;
|
|
mod context;
|
|
mod diagnostics;
|
|
mod eval;
|
|
mod parser;
|
|
pub use ast::{
|
|
AssignmentTarget, ExprId, Expression, ForEachExpr, ForEachKey, MapCase, MapCaseKey, MapKey, MatchCase,
|
|
MatchCaseKey, RegexSource, Statement,
|
|
};
|
|
pub use builtins::BuiltInFunction;
|
|
pub use diagnostics::{MappingDiagnostic, MappingOutcome};
|
|
#[cfg(test)]
|
|
use eval::eval_regex;
|
|
pub use eval::EvalResult;
|
|
pub use parser::Rule;
|