netget/server/srt/
streamid.rs1#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Target {
6 pub resource: String,
8 pub mode: String,
10 pub user: Option<String>,
11}
12
13pub fn parse(stream_id: &str) -> Result<Target, String> {
14 if stream_id.len() > 512 || crate::utils::sanitize::has_controls(&stream_id) {
15 return Err("stream ID is over 512 characters or holds a control character".into());
16 }
17 if let Some(list) = stream_id.strip_prefix("#!::") {
18 let mut t = Target {
19 resource: String::new(),
20 mode: "request".into(),
21 user: None,
22 };
23 for item in list.split(',').filter(|i| !i.is_empty()) {
24 let (k, v) = item
25 .split_once('=')
26 .ok_or_else(|| format!("{item:?} is not key=value"))?;
27 match k {
28 "r" => t.resource = v.to_owned(),
29 "m" => {
30 t.mode = match v {
31 "publish" => "publish",
32 "request" => "request",
33 "bidirectional" => return Err("bidirectional mode is not supported".into()),
34 other => return Err(format!("unknown mode {other:?}")),
35 }
36 .into()
37 }
38 "u" => t.user = Some(v.to_owned()),
39 _ => {}
40 }
41 }
42 return Ok(t);
43 }
44 for (prefix, mode) in [("publish:", "publish"), ("read:", "request")] {
45 if let Some(rest) = stream_id.strip_prefix(prefix) {
46 let mut parts = rest.split('?').next().unwrap_or_default().split(':');
48 let path = parts.next().unwrap_or_default();
49 let user = parts.next().filter(|u| !u.is_empty()).map(str::to_owned);
50 return Ok(Target {
51 resource: path.to_owned(),
52 mode: mode.into(),
53 user,
54 });
55 }
56 }
57 Ok(Target {
58 resource: stream_id.to_owned(),
59 mode: "request".into(),
60 user: None,
61 })
62}