microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/pipeline-issue-debugging

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/pip/src/fs.rs

211lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use std::{
5 path::{Path, PathBuf},
6 sync::Arc,
7};
8
9use miette::miette;
10use pyo3::{
11 exceptions::PyException,
12 prelude::*,
13 types::{PyDict, PyList, PyString, PyTuple},
14};
15use qsc::project::{DirEntry, EntryType, FileSystem};
16
17pub(crate) fn file_system(
18 py: Python,
19 read_file: pyo3::Py<PyAny>,
20 list_directory: pyo3::Py<PyAny>,
21 resolve_path: pyo3::Py<PyAny>,
22 fetch_github: pyo3::Py<PyAny>,
23) -> impl FileSystem {
24 Py {
25 py,
26 fs_hooks: FsHooks {
27 read_file,
28 list_directory,
29 resolve_path,
30 fetch_github,
31 },
32 }
33}
34
35struct FsHooks {
36 read_file: pyo3::Py<PyAny>,
37 list_directory: pyo3::Py<PyAny>,
38 resolve_path: pyo3::Py<PyAny>,
39 fetch_github: pyo3::Py<PyAny>,
40}
41
42#[derive(Debug)]
43struct Entry {
44 ty: EntryType,
45 path: String,
46 name: String,
47}
48
49impl DirEntry for Entry {
50 type Error = pyo3::PyErr;
51
52 fn entry_type(&self) -> Result<EntryType, Self::Error> {
53 Ok(self.ty)
54 }
55
56 fn entry_name(&self) -> String {
57 self.name.clone()
58 }
59
60 fn path(&self) -> PathBuf {
61 PathBuf::from(&self.path)
62 }
63}
64
65struct Py<'a> {
66 pub py: Python<'a>,
67 fs_hooks: FsHooks,
68}
69
70impl FileSystem for Py<'_> {
71 type Entry = Entry;
72
73 fn read_file(&self, path: &Path) -> miette::Result<(Arc<str>, Arc<str>)> {
74 read_file(self.py, &self.fs_hooks.read_file, path).map_err(|e| diagnostic_from(self.py, &e))
75 }
76
77 fn list_directory(&self, path: &Path) -> miette::Result<Vec<Self::Entry>> {
78 list_directory(self.py, &self.fs_hooks.list_directory, path)
79 .map_err(|e| diagnostic_from(self.py, &e))
80 }
81
82 fn resolve_path(&self, base: &Path, path: &Path) -> miette::Result<PathBuf> {
83 resolve_path(self.py, &self.fs_hooks.resolve_path, base, path)
84 .map_err(|e| diagnostic_from(self.py, &e))
85 }
86
87 fn fetch_github(
88 &self,
89 owner: &str,
90 repo: &str,
91 r#ref: &str,
92 path: &str,
93 ) -> miette::Result<Arc<str>> {
94 fetch_github(
95 self.py,
96 &self.fs_hooks.fetch_github,
97 owner,
98 repo,
99 r#ref,
100 path,
101 )
102 .map_err(|e| diagnostic_from(self.py, &e))
103 }
104}
105
106fn read_file(
107 py: Python,
108 read_file: &pyo3::Py<PyAny>,
109 path: &Path,
110) -> PyResult<(Arc<str>, Arc<str>)> {
111 let read_file_result = read_file.call1(py, PyTuple::new(py, &[path.to_string_lossy()])?)?;
112
113 let tuple = read_file_result.cast_bound::<PyTuple>(py)?;
114
115 Ok((get_tuple_string(tuple, 0)?, get_tuple_string(tuple, 1)?))
116}
117
118fn list_directory(
119 py: Python,
120 list_directory: &pyo3::Py<PyAny>,
121 path: &Path,
122) -> PyResult<Vec<Entry>> {
123 let list_directory_result =
124 list_directory.call1(py, PyTuple::new(py, &[path.to_string_lossy()])?)?;
125
126 list_directory_result
127 .cast_bound::<PyList>(py)?
128 .into_iter()
129 .map(|e| {
130 let dict = e.cast::<PyDict>()?;
131 let entry_type = match get_dict_string(dict, "type")?.to_string().as_str() {
132 "file" => EntryType::File,
133 "folder" => EntryType::Folder,
134 "symlink" => EntryType::Symlink,
135 _ => Err(PyException::new_err(
136 "expected valid value for `type` in list_directory result",
137 ))?,
138 };
139
140 Ok(Entry {
141 ty: entry_type,
142 path: get_dict_string(dict, "path")?.to_string(),
143 name: get_dict_string(dict, "entry_name")?.to_string(),
144 })
145 })
146 .collect() // Returns all values if all Ok, or first Err
147}
148
149fn resolve_path(
150 py: Python,
151 resolve_path: &pyo3::Py<PyAny>,
152 base: &Path,
153 path: &Path,
154) -> PyResult<PathBuf> {
155 let resolve_path_result = resolve_path.call1(
156 py,
157 PyTuple::new(py, &[base.to_string_lossy(), path.to_string_lossy()])?,
158 )?;
159
160 Ok(PathBuf::from(
161 resolve_path_result
162 .cast_bound::<PyString>(py)?
163 .str()?
164 .to_string(),
165 ))
166}
167
168fn fetch_github(
169 py: Python,
170 fetch_github: &pyo3::Py<PyAny>,
171 owner: &str,
172 repo: &str,
173 r#ref: &str,
174 path: &str,
175) -> PyResult<Arc<str>> {
176 let fetch_github_result =
177 fetch_github.call1(py, PyTuple::new(py, [owner, repo, r#ref, path])?)?;
178
179 Ok(fetch_github_result
180 .cast_bound::<PyString>(py)?
181 .to_string()
182 .into())
183}
184
185fn get_tuple_string(tuple: &Bound<'_, PyTuple>, index: usize) -> PyResult<Arc<str>> {
186 Ok(tuple
187 .get_item(index)?
188 .cast::<PyString>()?
189 .to_string()
190 .into())
191}
192
193fn get_dict_string<'a>(dict: &Bound<'a, PyDict>, key: &'a str) -> PyResult<Bound<'a, PyString>> {
194 match dict.get_item(key)? {
195 Some(item) => Ok(item.cast::<PyString>()?.str()?),
196 None => Err(PyException::new_err(format!("missing key `{key}` in dict"))),
197 }
198}
199
200fn diagnostic_from(py: Python<'_>, err: &PyErr) -> miette::Report {
201 if let Some(traceback) = err.traceback(py) {
202 match traceback.format() {
203 Ok(traceback) => miette!(format!("{err}\n{traceback}",)),
204 Err(traceback_err) => {
205 miette!(format!("{err}\nerror getting traceback: {traceback_err}",))
206 }
207 }
208 } else {
209 miette!(err.to_string())
210 }
211}
212