aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFenrir <[email protected]>2016-09-29 15:16:45 -0700
committerFenrir <[email protected]>2016-09-29 15:23:36 -0700
commited0c883b431033fe48aa251831c1358838c783c1 (patch)
treecaf663fc593f7d4372699825a92c5b755e4a871d /src
parentAdd create_dir_all function (diff)
downloadctru-rs-ed0c883b431033fe48aa251831c1358838c783c1.tar.xz
ctru-rs-ed0c883b431033fe48aa251831c1358838c783c1.zip
Documentation for the FS service
Diffstat (limited to 'src')
-rw-r--r--src/services/fs.rs303
1 files changed, 298 insertions, 5 deletions
diff --git a/src/services/fs.rs b/src/services/fs.rs
index 01df070..fb8309c 100644
--- a/src/services/fs.rs
+++ b/src/services/fs.rs
@@ -1,3 +1,9 @@
+//! Filesystem service
+//!
+//! This module contains basic methods to manipulate the contents of the 3DS's filesystem.
+//! Only the SD card is currently supported.
+
+
use core::marker::PhantomData;
use core::ptr;
use core::slice;
@@ -45,26 +51,106 @@ pub enum ArchiveID {
DemoSavedata,
}
-
+/// Represents the filesystem service. No file IO can be performed
+/// until an instance of this struct is created.
+///
+/// The service exits when this struct goes out of scope.
pub struct Fs {
pd: PhantomData<i32>,
}
+/// Handle to an open filesystem archive.
+///
+/// Archives are automatically closed when they go out of scope.
+///
+/// # Examples
+///
+/// ```no_run
+/// use ctru::services::fs::Fs
+///
+/// let fs = Fs::init().unwrap();
+/// let sdmc_archive = fs.sdmc().unwrap();
+/// ```
pub struct Archive {
id: ArchiveID,
handle: u64,
}
+/// A reference to an open file on the filesystem.
+///
+/// An instance of a `File` can be read and/or written to depending
+/// on what options It was opened with.
+///
+/// Files are automatically closed when they go out of scope.
pub struct File {
handle: u32,
offset: u64,
}
+/// Metadata information about a file.
+///
+/// This structure is returned from the [`metadata`] function and
+/// represents known metadata about a file.
+///
+/// [`metadata`]: fn.metadata.html
pub struct Metadata {
attributes: u32,
size: u64,
}
+/// Options and flags which can be used to configure how a [`File`] is opened.
+/// This builder exposes the ability to configure how a `File` is opened
+/// and what operations are permitted on the open file. The [`File::open`]
+/// and [`File::create`] methods are aliases for commonly used options
+/// using this builder.
+///
+/// [`File`]: struct.File.html
+/// [`File::open`]: struct.File.html#method.open
+/// [`File::create`]: struct.File.html#method.create
+///
+/// Generally speaking, when using `OpenOptions`, you'll first call [`new()`],
+/// then chain calls to methods to set each option, then call [`open()`],
+/// passing the path of the file you're trying to open.
+///
+/// It is required to also pass a reference to the [`Archive`] that the
+/// file lives in.
+///
+/// [`new()`]: struct.OpenOptions.html#method.new
+/// [`open()`]: struct.OpenOptions.html#method.open
+/// [`Archive`]: struct.Archive.html
+///
+/// # Examples
+///
+/// Opening a file to read:
+///
+/// ```no_run
+/// use ctru::services::fs::OpenOptions;
+///
+/// let fs = Fs::init().unwrap();
+/// let sdmc_archive = fs.sdmc().unwrap();
+/// let file = OpenOptions::new()
+/// .read(true)
+/// .archive(&sdmc_archive)
+/// .open("foo.txt")
+/// .unwrap();
+/// ```
+///
+/// Opening a file for both reading and writing, as well as creating it if it
+/// doesn't exist:
+///
+/// ```no_run
+/// use ctru::services::fs::OpenOptions;
+///
+/// let fs = Fs::init().unwrap();
+/// let sdmc_archive = fs.sdmc().unwrap();
+/// let file = OpenOptions::new()
+/// .read(true)
+/// .write(true)
+/// .create(true)
+/// .archive(&sdmc_archive)
+/// .open("foo.txt")
+/// .unwrap();
+/// ```
#[derive(Clone)]
pub struct OpenOptions {
read: bool,
@@ -73,24 +159,55 @@ pub struct OpenOptions {
arch_handle: u64,
}
+/// Iterator over the entries in a directory.
+///
+/// This iterator is returned from the [`read_dir`] function of this module and
+/// will yield instances of `Result<DirEntry, i32>`. Through a [`DirEntry`]
+/// information like the entry's path and possibly other metadata can be
+/// learned.
+///
+/// [`read_dir`]: fn.read_dir.html
+/// [`DirEntry`]: struct.DirEntry.html
+///
+/// # Errors
+///
+/// This Result will return Err if there's some sort of intermittent IO error
+/// during iteration.
pub struct ReadDir<'a> {
handle: Dir,
root: Arc<PathBuf>,
arch: &'a Archive,
}
+/// Entries returned by the [`ReadDir`] iterator.
+///
+/// [`ReadDir`]: struct.ReadDir.html
+///
+/// An instance of `DirEntry` represents an entry inside of a directory on the
+/// filesystem. Each entry can be inspected via methods to learn about the full
+/// path or possibly other metadata.
pub struct DirEntry<'a> {
entry: FS_DirectoryEntry,
root: Arc<PathBuf>,
arch: &'a Archive,
}
+#[doc(hidden)]
struct Dir(u32);
+#[doc(hidden)]
unsafe impl Send for Dir {}
+#[doc(hidden)]
unsafe impl Sync for Dir {}
impl Fs {
+ /// Initializes the FS service.
+ ///
+ /// # Errors
+ ///
+ /// This function will return Err if there was an error initializing the
+ /// FS service. This typically reflects a problem with the execution
+ /// environment and not necessarily your program itself.
pub fn init() -> Result<Fs, i32> {
unsafe {
let r = fsInit();
@@ -102,6 +219,7 @@ impl Fs {
}
}
+ /// Returns a handle to the SDMC (memory card) Archive.
pub fn sdmc(&self) -> Result<Archive, i32> {
unsafe {
let mut handle = 0;
@@ -121,23 +239,73 @@ impl Fs {
}
impl Archive {
+ /// Retrieves an Archive's [`ArchiveID`]
+ ///
+ /// [`ArchiveID`]: enum.ArchiveID.html
pub fn get_id(&self) -> ArchiveID {
self.id
}
}
impl File {
+ /// Attempts to open a file in read-only mode.
+ ///
+ /// See the [`OpenOptions::open`] method for more details.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if `path` does not already exit.
+ /// Other errors may also be returned accoridng to [`OpenOptions::open`]
+ ///
+ /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use ctru::servies::fs::{Fs, File};
+ ///
+ /// let fs = Fs::init().unwrap()
+ /// let sdmc_archive = fs.sdmc().unwrap()
+ /// let mut f = File::open("/foo.txt").unwrap();
+ /// ```
pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<File, i32> {
OpenOptions::new().read(true).archive(arch).open(path.as_ref())
}
+ /// Opens a file in write-only mode.
+ ///
+ /// This function will create a file if it does not exist.
+ ///
+ /// See the [`OpenOptions::create`] method for more details.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if `path` does not already exit.
+ /// Other errors may also be returned accoridng to [`OpenOptions::create`]
+ ///
+ /// [`OpenOptions::create`]: struct.OpenOptions.html#method.create
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use ctru::servies::fs::{Fs, File};
+ ///
+ /// let fs = Fs::init().unwrap()
+ /// let sdmc_archive = fs.sdmc().unwrap()
+ /// let mut f = File::create("/foo.txt").unwrap();
+ /// ```
pub fn create<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<File, i32> {
OpenOptions::new().write(true).create(true).archive(arch).open(path.as_ref())
}
- pub fn set_len(&mut self, len: u64) -> Result<(), i32> {
+ /// Truncates or extends the underlying file, updating the size of this file to become size.
+ ///
+ /// If the size is less than the current file's size, then the file will be shrunk. If it is
+ /// greater than the current file's size, then the file will be extended to size and have all
+ /// of the intermediate data filled in with 0s.
+ pub fn set_len(&mut self, size: u64) -> Result<(), i32> {
unsafe {
- let r = FSFILE_SetSize(self.handle, len);
+ let r = FSFILE_SetSize(self.handle, size);
if r < 0 {
Err(r)
} else {
@@ -146,9 +314,10 @@ impl File {
}
}
- // Right now the only file metadata we really have is file size
- // This will probably expand later on
+ /// Queries metadata about the underlying file.
pub fn metadata(&self) -> Result<Metadata, i32> {
+ // The only metadata we have for files right now is file size.
+ // This is likely to change in the future.
unsafe {
let mut size = 0;
let r = FSFILE_GetSize(self.handle, &mut size);
@@ -160,6 +329,11 @@ impl File {
}
}
+ /// Pull some bytes from the file into the specified buffer, returning
+ /// how many bytes were read.
+ ///
+ /// This function will become private when std::io support is ported
+ /// to this library.
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, i32> {
unsafe {
let mut n_read = 0;
@@ -179,12 +353,20 @@ impl File {
}
}
+ /// Read all bytes until EOF in this source, placing them into buf.
+ ///
+ /// This function will become private when std::io support is ported
+ /// to this library.
pub fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, i32> {
unsafe {
read_to_end_uninitialized(self, buf)
}
}
+ /// Write a buffer into this object, returning how many bytes were written.
+ ///
+ /// This function will become private when std::io support is ported
+ /// to this library.
pub fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
unsafe {
let mut n_written = 0;
@@ -207,20 +389,28 @@ impl File {
}
impl Metadata {
+ /// Returns whether this metadata is for a directory.
pub fn is_dir(&self) -> bool {
self.attributes == self.attributes | FS_ATTRIBUTE_DIRECTORY
}
+ /// Returns whether this metadata is for a regular file.
pub fn is_file(&self) -> bool {
!self.is_dir()
}
+ /// Returns the size, in bytes, this metadata is for.
+ ///
+ /// Directories return size = 0.
pub fn len(&self) -> u64 {
self.size
}
}
impl OpenOptions {
+ /// Creates a blank set of options ready for configuration.
+ ///
+ /// All options are initially set to `false`
pub fn new() -> OpenOptions {
OpenOptions {
read: false,
@@ -230,26 +420,61 @@ impl OpenOptions {
}
}
+ /// Sets the option for read access.
+ ///
+ /// This option, when true, will indicate that the file should be
+ /// `read`-able if opened.
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
self.read = read;
self
}
+ /// Sets the option for write access.
+ ///
+ /// This option, when true, will indicate that the file should be
+ /// `write`-able if opened.
+ ///
+ /// If the file already exists, any write calls on it will overwrite
+ /// its contents, without truncating it.
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
self.write = write;
self
}
+ /// Sets the option for creating a new file.
+ ///
+ /// This option indicates whether a new file will be created
+ /// if the file does not yet already
+ /// exist.
+ ///
+ /// In order for the file to be created, write access must also be used.
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
self.create = create;
self
}
+ /// Sets which archive the file is to be opened in.
+ ///
+ /// Failing to pass in an archive will result in the file failing to open.
pub fn archive(&mut self, archive: &Archive) -> &mut OpenOptions {
self.arch_handle = archive.handle;
self
}
+ /// Opens a file at `path` with the options specified by `self`
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error under a number of different
+ /// circumstances, including but not limited to:
+ ///
+ /// * Opening a file that doesn't exist without setting `create`.
+ /// * Attempting to open a file without passing an [`Archive`] reference
+ /// to the `archive` method.
+ /// * Filesystem-level errors (full disk, etc).
+ /// * Invalid combinations of open options.
+ ///
+ /// [`Archive`]: struct.Archive.html
pub fn open<P: AsRef<Path>>(&self, path: P) -> Result<File, i32> {
self._open(path.as_ref(), self.get_open_flags())
}
@@ -310,20 +535,34 @@ impl<'a> Iterator for ReadDir<'a> {
}
impl<'a> DirEntry<'a> {
+ /// Returns the full path to the file that this entry represents.
+ ///
+ /// The full path is created by joining the original path to `read_dir`
+ /// with the filename of this entry.
pub fn path(&self) -> PathBuf {
self.root.join(&self.file_name())
}
+ /// Return the metadata for the file that this entry points at.
pub fn metadata(&self) -> Result<Metadata, i32> {
metadata(self.arch, self.path())
}
+ /// Return the file type for the file that this entry points at.
pub fn file_name(&self) -> OsString {
let filename = truncate_utf16_at_nul(&self.entry.name);
OsString::from_wide(filename)
}
}
+/// Creates a new, empty directory at the provided path
+///
+/// # Errors
+///
+/// This function will return an error in the following situations,
+/// but is not limited to just these cases:
+///
+/// * User lacks permissions to create directory at `path`
pub fn create_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
@@ -337,6 +576,15 @@ pub fn create_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
}
}
+/// Recursively create a directory and all of its parent components if they are missing.
+///
+/// # Errors
+///
+/// This function will return an error in the following situations,
+/// but is not limited to just these cases:
+///
+/// * If any directory in the path specified by `path` does not already exist
+/// and it could not be created otherwise.
pub fn create_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
let path = path.as_ref();
let mut dir = PathBuf::new();
@@ -350,6 +598,7 @@ pub fn create_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32
result
}
+/// Given a path, query the file system to get information about a file, directory, etc
pub fn metadata<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<Metadata, i32> {
let maybe_file = File::open(&arch, path.as_ref());
let maybe_dir = read_dir(&arch, path.as_ref());
@@ -360,6 +609,15 @@ pub fn metadata<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<Metadata, i32
}
}
+/// Removes an existing, empty directory.
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not limited to just
+/// these cases:
+///
+/// * The user lacks permissions to remove the directory at the provided path.
+/// * The directory isn't empty.
pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
@@ -373,6 +631,11 @@ pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
}
}
+/// Removes a directory at this path, after removing all its contents. Use carefully!
+///
+/// # Errors
+///
+/// see `file::remove_file` and `fs::remove_dir`
pub fn remove_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
@@ -386,6 +649,17 @@ pub fn remove_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32
}
}
+/// Returns an iterator over the entries within a directory.
+///
+/// The iterator will yield instances of Result<DirEntry, i32>. New errors
+/// may be encountered after an iterator is initially constructed.
+///
+/// This function will return an error in the following situations, but is not limited to just
+/// these cases:
+///
+/// * The provided path doesn't exist.
+/// * The process lacks permissions to view the contents.
+/// * The path points at a non-directory file.
pub fn read_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<ReadDir, i32> {
unsafe {
let mut handle = 0;
@@ -401,6 +675,15 @@ pub fn read_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<ReadDir, i32>
}
}
+/// Removes a file from the filesystem.
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not limited to just
+/// these cases:
+///
+/// * path points to a directory.
+/// * The user lacks permissions to remove the file.
pub fn remove_file<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
@@ -414,6 +697,16 @@ pub fn remove_file<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
}
}
+/// Rename a file or directory to a new name, replacing the original file
+/// if to already exists.
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not limited to just
+/// these cases:
+///
+/// * from does not exist.
+/// * The user lacks permissions to view contents.
pub fn rename<P, Q>(arch: &Archive, from: P, to: Q) -> Result<(), i32>
where P: AsRef<Path>,
Q: AsRef<Path> {