microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7421e7dd1015dcbd940bf843d33583470de580ea

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/src/fs.rs

203lines · 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: PyObject,
20 list_directory: PyObject,
21 resolve_path: PyObject,
22 fetch_github: PyObject,
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: PyObject,
37 list_directory: PyObject,
38 resolve_path: PyObject,
39 fetch_github: PyObject,
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(py: Python, read_file: &PyObject, path: &Path) -> PyResult<(Arc<str>, Arc<str>)> {
107 let read_file_result = read_file.call1(py, PyTuple::new(py, &[path.to_string_lossy()])?)?;
108
109 let tuple = read_file_result.downcast_bound::<PyTuple>(py)?;
110
111 Ok((get_tuple_string(tuple, 0)?, get_tuple_string(tuple, 1)?))
112}
113
114fn list_directory(py: Python, list_directory: &PyObject, path: &Path) -> PyResult<Vec<Entry>> {
115 let list_directory_result =
116 list_directory.call1(py, PyTuple::new(py, &[path.to_string_lossy()])?)?;
117
118 list_directory_result
119 .downcast_bound::<PyList>(py)?
120 .into_iter()
121 .map(|e| {
122 let dict = e.downcast::<PyDict>()?;
123 let entry_type = match get_dict_string(dict, "type")?.to_string().as_str() {
124 "file" => EntryType::File,
125 "folder" => EntryType::Folder,
126 "symlink" => EntryType::Symlink,
127 _ => Err(PyException::new_err(
128 "expected valid value for `type` in list_directory result",
129 ))?,
130 };
131
132 Ok(Entry {
133 ty: entry_type,
134 path: get_dict_string(dict, "path")?.to_string(),
135 name: get_dict_string(dict, "entry_name")?.to_string(),
136 })
137 })
138 .collect() // Returns all values if all Ok, or first Err
139}
140
141fn resolve_path(
142 py: Python,
143 resolve_path: &PyObject,
144 base: &Path,
145 path: &Path,
146) -> PyResult<PathBuf> {
147 let resolve_path_result = resolve_path.call1(
148 py,
149 PyTuple::new(py, &[base.to_string_lossy(), path.to_string_lossy()])?,
150 )?;
151
152 Ok(PathBuf::from(
153 resolve_path_result
154 .downcast_bound::<PyString>(py)?
155 .str()?
156 .to_string(),
157 ))
158}
159
160fn fetch_github(
161 py: Python,
162 fetch_github: &PyObject,
163 owner: &str,
164 repo: &str,
165 r#ref: &str,
166 path: &str,
167) -> PyResult<Arc<str>> {
168 let fetch_github_result =
169 fetch_github.call1(py, PyTuple::new(py, [owner, repo, r#ref, path])?)?;
170
171 Ok(fetch_github_result
172 .downcast_bound::<PyString>(py)?
173 .to_string()
174 .into())
175}
176
177fn get_tuple_string(tuple: &Bound<'_, PyTuple>, index: usize) -> PyResult<Arc<str>> {
178 Ok(tuple
179 .get_item(index)?
180 .downcast::<PyString>()?
181 .to_string()
182 .into())
183}
184
185fn get_dict_string<'a>(dict: &Bound<'a, PyDict>, key: &'a str) -> PyResult<Bound<'a, PyString>> {
186 match dict.get_item(key)? {
187 Some(item) => Ok(item.downcast::<PyString>()?.str()?),
188 None => Err(PyException::new_err(format!("missing key `{key}` in dict"))),
189 }
190}
191
192fn diagnostic_from(py: Python<'_>, err: &PyErr) -> miette::Report {
193 if let Some(traceback) = err.traceback(py) {
194 match traceback.format() {
195 Ok(traceback) => miette!(format!("{err}\n{traceback}",)),
196 Err(traceback_err) => {
197 miette!(format!("{err}\nerror getting traceback: {traceback_err}",))
198 }
199 }
200 } else {
201 miette!(err.to_string())
202 }
203}
204