uri.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. package storage
  2. import (
  3. "fyne.io/fyne/v2"
  4. "fyne.io/fyne/v2/storage/repository"
  5. )
  6. // NewFileURI creates a new URI from the given file path.
  7. func NewFileURI(path string) fyne.URI {
  8. return repository.NewFileURI(path)
  9. }
  10. // NewURI creates a new URI from the given string representation. This could be
  11. // a URI from an external source or one saved from URI.String()
  12. //
  13. // Deprecated: use ParseURI instead
  14. func NewURI(s string) fyne.URI {
  15. u, _ := ParseURI(s)
  16. return u
  17. }
  18. // ParseURI creates a new URI instance by parsing a URI string.
  19. //
  20. // Parse URI will parse up to the first ':' present in the URI string to
  21. // extract the scheme, and then delegate further parsing to the registered
  22. // repository for the given scheme. If no repository is registered for that
  23. // scheme, the URI is parsed on a best-effort basis using net/url.
  24. //
  25. // As a special exception, URIs beginning with 'file:' are always parsed using
  26. // NewFileURI(), which will correctly handle back-slashes appearing in the URI
  27. // path component on Windows.
  28. //
  29. // Since: 2.0
  30. func ParseURI(s string) (fyne.URI, error) {
  31. return repository.ParseURI(s)
  32. }
  33. // Parent returns a URI referencing the parent resource of the resource
  34. // referenced by the URI. For example, the Parent() of 'file://foo/bar.baz' is
  35. // 'file://foo'. The URI which is returned will be listable.
  36. //
  37. // NOTE: it is not a given that Parent() return a parent URI with the same
  38. // Scheme(), though this will normally be the case.
  39. //
  40. // This can fail in several ways:
  41. //
  42. // - If the URI refers to a filesystem root, then the Parent() implementation
  43. // must return (nil, URIRootError).
  44. //
  45. // - If the URI refers to a resource which does not exist in a hierarchical
  46. // context (e.g. the URI references something which does not have a
  47. // semantically meaningful "parent"), the Parent() implementation may return
  48. // an error.
  49. //
  50. // - If determining the parent of the referenced resource requires
  51. // interfacing with some external system, failures may propagate
  52. // through the Parent() implementation. For example if determining
  53. // the parent of a file:// URI requires reading information from
  54. // the filesystem, it could fail with a permission error.
  55. //
  56. // - If the scheme of the given URI does not have a registered
  57. // HierarchicalRepository instance, then this method will fail with a
  58. // repository.ErrOperationNotSupported.
  59. //
  60. // NOTE: since v2.0.0, Parent() is backed by the repository system - this
  61. // function is a helper which calls into an appropriate repository instance for
  62. // the scheme of the URI it is given.
  63. //
  64. // Since: 1.4
  65. func Parent(u fyne.URI) (fyne.URI, error) {
  66. repo, err := repository.ForURI(u)
  67. if err != nil {
  68. return nil, err
  69. }
  70. hrepo, ok := repo.(repository.HierarchicalRepository)
  71. if !ok {
  72. return nil, repository.ErrOperationNotSupported
  73. }
  74. return hrepo.Parent(u)
  75. }
  76. // Child returns a URI referencing a resource nested hierarchically below the
  77. // given URI, identified by a string. For example, the child with the string
  78. // component 'quux' of 'file://foo/bar' is 'file://foo/bar/quux'.
  79. //
  80. // This can fail in several ways:
  81. //
  82. // - If the URI refers to a resource which does not exist in a hierarchical
  83. // context (e.g. the URI references something which does not have a
  84. // semantically meaningful "child"), the Child() implementation may return an
  85. // error.
  86. //
  87. // - If generating a reference to a child of the referenced resource requires
  88. // interfacing with some external system, failures may propagate through the
  89. // Child() implementation. It is expected that this case would occur very
  90. // rarely if ever.
  91. //
  92. // - If the scheme of the given URI does not have a registered
  93. // HierarchicalRepository instance, then this method will fail with a
  94. // repository.ErrOperationNotSupported.
  95. //
  96. // NOTE: since v2.0.0, Child() is backed by the repository system - this
  97. // function is a helper which calls into an appropriate repository instance for
  98. // the scheme of the URI it is given.
  99. //
  100. // Since: 1.4
  101. func Child(u fyne.URI, component string) (fyne.URI, error) {
  102. repo, err := repository.ForURI(u)
  103. if err != nil {
  104. return nil, err
  105. }
  106. hrepo, ok := repo.(repository.HierarchicalRepository)
  107. if !ok {
  108. return nil, repository.ErrOperationNotSupported
  109. }
  110. return hrepo.Child(u, component)
  111. }
  112. // Exists determines if the resource referenced by the URI exists.
  113. //
  114. // This can fail in several ways:
  115. //
  116. // - If checking the existence of a resource requires interfacing with some
  117. // external system, then failures may propagate through Exists(). For
  118. // example, checking the existence of a resource requires reading a directory
  119. // may result in a permissions error.
  120. //
  121. // It is understood that a non-nil error value signals that the existence or
  122. // non-existence of the resource cannot be determined and is undefined.
  123. //
  124. // NOTE: since v2.0.0, Exists is backed by the repository system - this function
  125. // calls into a scheme-specific implementation from a registered repository.
  126. //
  127. // Exists may call into either a generic implementation, or into a
  128. // scheme-specific implementation depending on which storage repositories have
  129. // been registered.
  130. //
  131. // Since: 1.4
  132. func Exists(u fyne.URI) (bool, error) {
  133. repo, err := repository.ForURI(u)
  134. if err != nil {
  135. return false, err
  136. }
  137. return repo.Exists(u)
  138. }
  139. // Delete destroys, deletes, or otherwise removes the resource referenced
  140. // by the URI.
  141. //
  142. // This can fail in several ways:
  143. //
  144. // - If removing the resource requires interfacing with some external system,
  145. // failures may propagate through Destroy(). For example, deleting a file may
  146. // fail with a permissions error.
  147. //
  148. // - If the referenced resource does not exist, attempting to destroy it should
  149. // throw an error.
  150. //
  151. // - If the scheme of the given URI does not have a registered
  152. // WritableRepository instance, then this method will fail with a
  153. // repository.ErrOperationNotSupported.
  154. //
  155. // Delete is backed by the repository system - this function calls
  156. // into a scheme-specific implementation from a registered repository.
  157. //
  158. // Since: 2.0
  159. func Delete(u fyne.URI) error {
  160. repo, err := repository.ForURI(u)
  161. if err != nil {
  162. return err
  163. }
  164. wrepo, ok := repo.(repository.WritableRepository)
  165. if !ok {
  166. return repository.ErrOperationNotSupported
  167. }
  168. return wrepo.Delete(u)
  169. }
  170. // Reader returns URIReadCloser set up to read from the resource that the
  171. // URI references.
  172. //
  173. // This method can fail in several ways:
  174. //
  175. // - Different permissions or credentials are required to read the
  176. // referenced resource.
  177. //
  178. // - This URI scheme could represent some resources that can be read,
  179. // but this particular URI references a resources that is not
  180. // something that can be read.
  181. //
  182. // - Attempting to set up the reader depended on a lower level
  183. // operation such as a network or filesystem access that has failed
  184. // in some way.
  185. //
  186. // Reader is backed by the repository system - this function calls
  187. // into a scheme-specific implementation from a registered repository.
  188. //
  189. // Since: 2.0
  190. func Reader(u fyne.URI) (fyne.URIReadCloser, error) {
  191. repo, err := repository.ForURI(u)
  192. if err != nil {
  193. return nil, err
  194. }
  195. return repo.Reader(u)
  196. }
  197. // CanRead determines if a given URI could be written to using the Reader()
  198. // method. It is preferred to check if a URI is readable using this method
  199. // before calling Reader(), because the underlying operations required to
  200. // attempt to read and then report an error may be slower than the operations
  201. // needed to test if a URI is readable. Keep in mind however that even if
  202. // CanRead returns true, you must still do appropriate error handling for
  203. // Reader(), as the underlying filesystem may have changed since you called
  204. // CanRead.
  205. //
  206. // The non-existence of a resource should not be treated as an error. In other
  207. // words, a Repository implementation which for some URI u returns false, nil
  208. // for Exists(u), CanRead(u) should also return false, nil.
  209. //
  210. // CanRead is backed by the repository system - this function calls into a
  211. // scheme-specific implementation from a registered repository.
  212. //
  213. // Since: 2.0
  214. func CanRead(u fyne.URI) (bool, error) {
  215. repo, err := repository.ForURI(u)
  216. if err != nil {
  217. return false, err
  218. }
  219. return repo.CanRead(u)
  220. }
  221. // Writer returns URIWriteCloser set up to write to the resource that the
  222. // URI references.
  223. //
  224. // Writing to a non-extant resource should create that resource if possible
  225. // (and if not possible, this should be reflected in the return of CanWrite()).
  226. // Writing to an extant resource should overwrite it in-place. At present, this
  227. // API does not provide a mechanism for appending to an already-extant
  228. // resource, except for reading it in and writing all the data back out.
  229. //
  230. // This method can fail in several ways:
  231. //
  232. // - Different permissions or credentials are required to write to the
  233. // referenced resource.
  234. //
  235. // - This URI scheme could represent some resources that can be
  236. // written, but this particular URI references a resources that is
  237. // not something that can be written.
  238. //
  239. // - Attempting to set up the writer depended on a lower level
  240. // operation such as a network or filesystem access that has failed
  241. // in some way.
  242. //
  243. // - If the scheme of the given URI does not have a registered
  244. // WritableRepository instance, then this method will fail with a
  245. // repository.ErrOperationNotSupported.
  246. //
  247. // Writer is backed by the repository system - this function calls into a
  248. // scheme-specific implementation from a registered repository.
  249. //
  250. // Since: 2.0
  251. func Writer(u fyne.URI) (fyne.URIWriteCloser, error) {
  252. repo, err := repository.ForURI(u)
  253. if err != nil {
  254. return nil, err
  255. }
  256. wrepo, ok := repo.(repository.WritableRepository)
  257. if !ok {
  258. return nil, repository.ErrOperationNotSupported
  259. }
  260. return wrepo.Writer(u)
  261. }
  262. // CanWrite determines if a given URI could be written to using the Writer()
  263. // method. It is preferred to check if a URI is writable using this method
  264. // before calling Writer(), because the underlying operations required to
  265. // attempt to write and then report an error may be slower than the operations
  266. // needed to test if a URI is writable. Keep in mind however that even if
  267. // CanWrite returns true, you must still do appropriate error handling for
  268. // Writer(), as the underlying filesystem may have changed since you called
  269. // CanWrite.
  270. // CanWrite is backed by the repository system - this function calls into a
  271. // scheme-specific implementation from a registered repository.
  272. //
  273. // Since: 2.0
  274. func CanWrite(u fyne.URI) (bool, error) {
  275. repo, err := repository.ForURI(u)
  276. if err != nil {
  277. return false, err
  278. }
  279. wrepo, ok := repo.(repository.WritableRepository)
  280. if !ok {
  281. return false, repository.ErrOperationNotSupported
  282. }
  283. return wrepo.CanWrite(u)
  284. }
  285. // Copy given two URIs, 'src', and 'dest' both of the same scheme, will copy
  286. // one to the other. If the source and destination are of different schemes,
  287. // then the Copy implementation for the storage repository registered to the
  288. // scheme of the source will be used. Implementations are recommended to use
  289. // repository.GenericCopy() as a fail-over in the case that they do not
  290. // understand how to operate on the scheme of the destination URI. However, the
  291. // behavior of calling Copy() on URIs of non-matching schemes is ultimately
  292. // defined by the storage repository registered to the scheme of the source
  293. // URI.
  294. //
  295. // This method may fail in several ways:
  296. //
  297. // - Different permissions or credentials are required to perform the
  298. // copy operation.
  299. //
  300. // - This URI scheme could represent some resources that can be copied,
  301. // but either the source, destination, or both are not resources
  302. // that support copying.
  303. //
  304. // - Performing the copy operation depended on a lower level operation
  305. // such as network or filesystem access that has failed in some way.
  306. //
  307. // - If the scheme of the given URI does not have a registered
  308. // CopyableRepository instance, then this method will fail with a
  309. // repository.ErrOperationNotSupported.
  310. //
  311. // Copy is backed by the repository system - this function calls into a
  312. // scheme-specific implementation from a registered repository.
  313. //
  314. // Since: 2.0
  315. func Copy(source fyne.URI, destination fyne.URI) error {
  316. repo, err := repository.ForURI(source)
  317. if err != nil {
  318. return err
  319. }
  320. crepo, ok := repo.(repository.CopyableRepository)
  321. if !ok {
  322. return repository.ErrOperationNotSupported
  323. }
  324. return crepo.Copy(source, destination)
  325. }
  326. // Move returns a method that given two URIs, 'src' and 'dest' both of the same
  327. // scheme this will move src to dest. This means the resource referenced by
  328. // src will be copied into the resource referenced by dest, and the resource
  329. // referenced by src will no longer exist after the operation is complete.
  330. //
  331. // If the source and destination are of different schemes, then the Move
  332. // implementation for the storage repository registered to the scheme of the
  333. // source will be used. Implementations are recommended to use
  334. // repository.GenericMove() as a fail-over in the case that they do not
  335. // understand how to operate on the scheme of the destination URI. However, the
  336. // behavior of calling Move() on URIs of non-matching schemes is ultimately
  337. // defined by the storage repository registered to the scheme of the source
  338. // URI.
  339. //
  340. // This method may fail in several ways:
  341. //
  342. // - Different permissions or credentials are required to perform the
  343. // rename operation.
  344. //
  345. // - This URI scheme could represent some resources that can be renamed,
  346. // but either the source, destination, or both are not resources
  347. // that support renaming.
  348. //
  349. // - Performing the rename operation depended on a lower level operation
  350. // such as network or filesystem access that has failed in some way.
  351. //
  352. // - If the scheme of the given URI does not have a registered
  353. // MovableRepository instance, then this method will fail with a
  354. // repository.ErrOperationNotSupported.
  355. //
  356. // Move is backed by the repository system - this function calls into a
  357. // scheme-specific implementation from a registered repository.
  358. //
  359. // Since: 2.0
  360. func Move(source fyne.URI, destination fyne.URI) error {
  361. repo, err := repository.ForURI(source)
  362. if err != nil {
  363. return err
  364. }
  365. mrepo, ok := repo.(repository.MovableRepository)
  366. if !ok {
  367. return repository.ErrOperationNotSupported
  368. }
  369. return mrepo.Move(source, destination)
  370. }
  371. // CanList will determine if the URI is listable or not.
  372. //
  373. // This method may fail in several ways:
  374. //
  375. // - Different permissions or credentials are required to check if the
  376. // URI supports listing.
  377. //
  378. // - This URI scheme could represent some resources that can be listed,
  379. // but this specific URI is not one of them (e.g. a file on a
  380. // filesystem, as opposed to a directory).
  381. //
  382. // - Checking for listability depended on a lower level operation
  383. // such as network or filesystem access that has failed in some way.
  384. //
  385. // - If the scheme of the given URI does not have a registered
  386. // ListableRepository instance, then this method will fail with a
  387. // repository.ErrOperationNotSupported.
  388. //
  389. // CanList is backed by the repository system - this function calls into a
  390. // scheme-specific implementation from a registered repository.
  391. //
  392. // Since: 2.0
  393. func CanList(u fyne.URI) (bool, error) {
  394. repo, err := repository.ForURI(u)
  395. if err != nil {
  396. return false, err
  397. }
  398. lrepo, ok := repo.(repository.ListableRepository)
  399. if !ok {
  400. return false, repository.ErrOperationNotSupported
  401. }
  402. return lrepo.CanList(u)
  403. }
  404. // List returns a list of URIs that reference resources which are nested below
  405. // the resource referenced by the argument. For example, listing a directory on
  406. // a filesystem should return a list of files and directories it contains.
  407. //
  408. // This method may fail in several ways:
  409. //
  410. // - Different permissions or credentials are required to obtain a
  411. // listing for the given URI.
  412. //
  413. // - This URI scheme could represent some resources that can be listed,
  414. // but this specific URI is not one of them (e.g. a file on a
  415. // filesystem, as opposed to a directory). This can be tested in advance
  416. // using the Listable() function.
  417. //
  418. // - Obtaining the listing depended on a lower level operation such as
  419. // network or filesystem access that has failed in some way.
  420. //
  421. // - If the scheme of the given URI does not have a registered
  422. // ListableRepository instance, then this method will fail with a
  423. // repository.ErrOperationNotSupported.
  424. //
  425. // List is backed by the repository system - this function either calls into a
  426. // scheme-specific implementation from a registered repository, or fails with a
  427. // URIOperationNotSupported error.
  428. //
  429. // Since: 2.0
  430. func List(u fyne.URI) ([]fyne.URI, error) {
  431. repo, err := repository.ForURI(u)
  432. if err != nil {
  433. return nil, err
  434. }
  435. lrepo, ok := repo.(repository.ListableRepository)
  436. if !ok {
  437. return nil, repository.ErrOperationNotSupported
  438. }
  439. return lrepo.List(u)
  440. }
  441. // CreateListable creates a new listable resource referenced by the given URI.
  442. // CreateListable will error if the URI already references an extant resource.
  443. // This method is used for storage repositories where listable resources are of
  444. // a different underlying type than other resources - for example, in a typical
  445. // filesystem ('file://'), CreateListable() corresponds to directory creation,
  446. // and Writer() implies file creation for non-extant operands.
  447. //
  448. // For storage repositories where listable and non-listable resources are the
  449. // of the same underlying type, CreateListable should be equivalent to calling
  450. // Writer(), writing zero bytes, and then closing the `URIWriteCloser - in
  451. // filesystem terms, the same as calling 'touch;'.
  452. //
  453. // Storage repositories which support listing, but not creation of listable
  454. // objects may return repository.ErrOperationNotSupported.
  455. //
  456. // CreateListable should generally fail if the parent of it's operand does not
  457. // exist, however this can vary by the implementation details of the specific
  458. // storage repository. In filesystem terms, this function is "mkdir" not "mkdir
  459. // -p".
  460. //
  461. // This method may fail in several ways:
  462. //
  463. // - Different permissions or credentials are required to create the requested
  464. // resource.
  465. //
  466. // - Creating the resource depended on a lower level operation such as network
  467. // or filesystem access that has failed in some way.
  468. //
  469. // - If the scheme of the given URI does not have a registered
  470. // ListableRepository instance, then this method will fail with a
  471. // repository.ErrOperationNotSupported.
  472. //
  473. // CreateListable is backed by the repository system - this function either
  474. // calls into a scheme-specific implementation from a registered repository, or
  475. // fails with a URIOperationNotSupported error.
  476. //
  477. // Since: 2.0
  478. func CreateListable(u fyne.URI) error {
  479. repo, err := repository.ForURI(u)
  480. if err != nil {
  481. return err
  482. }
  483. lrepo, ok := repo.(repository.ListableRepository)
  484. if !ok {
  485. return repository.ErrOperationNotSupported
  486. }
  487. return lrepo.CreateListable(u)
  488. }