1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use super::*;
impl RaSvnSession {
/// Returns the node properties for a file or directory path.
pub async fn proplist(
&mut self,
path: &str,
rev: Option<u64>,
) -> Result<Option<PropertyList>, SvnError> {
let path = validate_rel_dir_path(path)?;
let kind = self.check_path(&path, rev).await?;
match kind {
NodeKind::None => Ok(None),
NodeKind::Unknown => Err(SvnError::Protocol("node kind unknown".into())),
NodeKind::File => {
let path = validate_rel_path(&path)?;
let props = self
.with_retry("get-file-proplist", move |conn| {
let path = path.clone();
Box::pin(async move {
let rev_tuple = match rev {
Some(r) => SvnItem::List(vec![SvnItem::Number(r)]),
None => SvnItem::List(Vec::new()),
};
let params = SvnItem::List(vec![
SvnItem::String(path.as_bytes().to_vec()),
rev_tuple,
SvnItem::Bool(true), // want-props
SvnItem::Bool(false), // want-contents
// The standard client always sends want-iprops as false and
// uses a separate `get-iprops` request (see protocol notes).
SvnItem::Bool(false),
]);
let response = conn.call("get-file", params).await?;
let params = response.success_params("get-file")?;
let meta = parse_get_file_response_params(params)?;
Ok(meta.props)
})
})
.await?;
Ok(Some(props))
}
NodeKind::Dir => {
let props = self
.with_retry("get-dir-proplist", move |conn| {
let path = path.clone();
Box::pin(async move {
let rev_tuple = match rev {
Some(r) => SvnItem::List(vec![SvnItem::Number(r)]),
None => SvnItem::List(Vec::new()),
};
let params = SvnItem::List(vec![
SvnItem::String(path.as_bytes().to_vec()),
rev_tuple,
SvnItem::Bool(true), // want-props
SvnItem::Bool(false), // want-contents
SvnItem::List(Vec::new()),
// The standard client always sends want-iprops as false and
// uses a separate `get-iprops` request (see protocol notes).
SvnItem::Bool(false),
]);
let response = conn.call("get-dir", params).await?;
let params = response.success_params("get-dir")?;
if params.len() < 2 {
return Err(SvnError::Protocol(
"get-dir response missing props".into(),
));
}
parse_proplist(¶ms[1])
})
})
.await?;
Ok(Some(props))
}
}
}
/// Returns a single property value for a file or directory path.
pub async fn propget(
&mut self,
path: &str,
rev: Option<u64>,
name: &str,
) -> Result<Option<Vec<u8>>, SvnError> {
let Some(props) = self.proplist(path, rev).await? else {
return Ok(None);
};
Ok(props.get(name).cloned())
}
/// Runs `get-iprops` and returns inherited properties for a path.
pub async fn inherited_props(
&mut self,
path: &str,
rev: Option<u64>,
) -> Result<Vec<InheritedProps>, SvnError> {
let path = validate_rel_dir_path(path)?;
self.with_retry("get-iprops", move |conn| {
let path = path.clone();
Box::pin(async move {
let rev_tuple = match rev {
Some(r) => SvnItem::List(vec![SvnItem::Number(r)]),
None => SvnItem::List(Vec::new()),
};
let params =
SvnItem::List(vec![SvnItem::String(path.as_bytes().to_vec()), rev_tuple]);
let response = conn.call("get-iprops", params).await?;
let params = response.success_params("get-iprops")?;
let iproplist = params.first().ok_or_else(|| {
SvnError::Protocol("get-iprops response missing iproplist".into())
})?;
parse_iproplist(iproplist)
})
})
.await
}
}