Struct Phazer

Source
pub struct Phazer<'cs> { /* private fields */ }
Expand description

Phazer manages the transition of the working file to the target file.

Phazer is the core component of this crate. One Phazer is constructed for each target file. Phazer essentially a wrapper over the target path. For example, if the application downloads three files from the internet then one Phazer is created for each file.

By default, Phazer uses a simple rename commit strategy (SIMPLE_RENAME_STRATEGY). When Phazer::commit is called, rename is used to replace the target file with the working file. PhazerBuilder can be used to construct a Phazer with a different commit strategy. The one other commit strategy available with this crate is RENAME_WITH_RETRY_STRATEGY.

Implementations§

Source§

impl<'cs> Phazer<'cs>

Source

pub fn simple_writer<'a>(&'a self) -> Result<SimplePhazerWriter<'_, '_>>

Returns a synchronous file-like thing that’s used to build the working file.

In addition to managing the transition from working file to target file (a commit), Phazer provides a way to build the working file. That process starts here with the creation of a SimplePhazerWriter. If a working file has not yet been created this method creates the working file. If a working file exists this method opens the existing file for read / write access.

The working file cannot be open when Phazer::commit is called. This is enforced by a lifetime connecting each SimplePhazerWriter to the Phazer that created it. If Phazer::commit is called when a SimplePhazerWriter is active an error similar to the following occurs when compiling…

    error[E0505]: cannot move out of 'phazer' because it is borrowed

This method is available when the simple feature is enabled.

§Return Value

An Error is returned if the working file cannot be created or opened for read / write access. Otherwise a new SimplePhazerWriter is returned that provides access to the working file.

§Example
use std::fs::canonicalize;
use std::io::Write;

use phazer::Phazer;

pub fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Use a full path so we can freely change the working directory
    let full_path = canonicalize("config.toml")?;
    // Create the Phazer
    let phazer = Phazer::new(&full_path);
    // Write some stuff.  Drop the writer to ensure the file is not open.
    let mut writer = phazer.simple_writer()?;
    writer.write_all("[Serial Port]\nbaud = 250000\n".as_bytes())?;
    drop(writer);
    // Rename the working file to the target file ("save" the changes)
    phazer.commit()?;
    Ok(())
}
Source§

impl<'cs> Phazer<'cs>

Source

pub async fn tokio_writer<'a>(&'a self) -> Result<TokioPhazerWriter<'_, '_>>

Returns an asynchronous file-like thing that’s used to build the working file.

In addition to managing the transition from working file to target file (a commit), Phazer provides a way to build the working file. That process starts here with the creation of a TokioPhazerWriter. If a working file has not yet been created this method creates the working file. If a working file exists this method opens the existing file for read / write access.

The working file cannot be open when Phazer::commit is called. This is enforced by a lifetime connecting each TokioPhazerWriter to the Phazer that created it. If Phazer::commit is called when a TokioPhazerWriter is active an error similar to the following occurs when compiling…

    error[E0505]: cannot move out of 'phazer' because it is borrowed

This method is available when the tokio feature is enabled.

§Return Value

An Error is returned if the working file cannot be created or opened for read / write access. Otherwise a new TokioPhazerWriter is returned that provides access to the working file.

§Example
use tokio::fs::canonicalize;
use tokio::io::AsyncWriteExt;

use phazer::Phazer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Use a full path so we can freely change the working directory
    let full_path = canonicalize("config.toml").await?;
    // Create the Phazer
    let phazer = Phazer::new(&full_path);
    // Write some stuff.  Drop the writer to ensure the file is not open.
    let mut writer = phazer.tokio_writer().await?;
    writer.write_all("[Serial Port]\nbaud = 250000\n".as_bytes()).await?;
    drop(writer);
    // Rename the working file to the target file ("save" the changes)
    phazer.commit()?;
    Ok(())
}
Source§

impl<'cs> Phazer<'cs>

Source

pub fn new<P>(path: P) -> Self
where P: Into<PathBuf>,

Creates a Phazer where path is the target file.

§Arguments
  • path - Target file. Ideally, the full path is specified so changes to the working directory do not cause problems. canonicalize is helpful.
§Return Value

A new Phazer is always returned; Phazer::new is infallible.

Source

pub fn commit(self) -> Result<(), Error>

commit transfers the working file to the target file; by default this is done with a rename.

If the working file was not created then commit simply returns Ok(()).

The working file cannot be open when commit is called. This is enforced by a lifetime connecting each writer to the Phazer that created it. If commit is called when a writer is active then an error similar to the following occurs when compiling…

    error[E0505]: cannot move out of 'phazer' because it is borrowed

Often, writers must be explicitly dropped before calling commit. This is shown in the example.

§Return Value

An Error is returned if the working file cannot be transferred to the target file. In this case the working file is removed.

§Example
use std::fs::canonicalize;
use std::io::Write;

use phazer::Phazer;

pub fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Use a full path so we can freely change the working directory
    let target = canonicalize("config.toml")?;
    // Create the Phazer
    let phazer = Phazer::new(&target);
    // Write some stuff.  Drop the writer to ensure the file is not open.
    let mut writer = phazer.simple_writer()?;
    writer.write_all("[Serial Port]\nbaud = 250000\n".as_bytes())?;

    // Note the explicit `drop`.  This ensures `writer` does not exist when `commit` is
    // called.  That the working file is no longer open.
    drop(writer);

    // Rename the working file to the target file ("save" the changes)
    phazer.commit()?;
    Ok(())
}
Source

pub fn commit2(self) -> Result<(), (Error, Phazer<'cs>)>

commit2 transfers the working file to the target file; by default this is done with a rename.

If the working file was not created then commit2 simply returns Ok(()).

The working file cannot be open when commit2 is called. This is enforced by a lifetime connecting each writer to the Phazer that created it. If commit2 is called when a writer is active then an error similar to the following occurs when compiling…

    error[E0505]: cannot move out of 'phazer' because it is borrowed

Often, writers must be explicitly dropped before calling commit2. This is shown in the example.

§Return Value

An Error and the Phazer are returned if the working file cannot be transferred to the target file. This allows for error recovery not provided by this crate. For example, on Windows, a target file with the read-only attribute set cannot be replaced with a rename. This is demonstrated in the example.

§Example
use std::fs::{canonicalize, metadata, set_permissions};
use std::io::{ErrorKind, Write};
use std::path::Path;

use phazer::Phazer;

fn set_readonly<P: AsRef<Path>>(path: P, value: bool) -> Result<bool, std::io::Error> {
    let path = path.as_ref();
    let m = metadata(path)?;
    let mut p = m.permissions();
    let rv = p.readonly();
    p.set_readonly(value);
    set_permissions(path, p)?;
    Ok(rv)
}

pub fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Use a full path so we can freely change the working directory
    let target = canonicalize("read-only-windows.txt")?;

    // Create the target
    let phazer = Phazer::new(&target);
    let mut writer = phazer.simple_writer()?;
    writer.write_all("read-only".as_bytes())?;
    drop(writer);
    phazer.commit()?;

    // Set the read-only attribute
    set_readonly(&target, true)?;

    // Try to update it...
    let phazer = Phazer::new(&target);
    let mut writer = phazer.simple_writer()?;
    writer.write_all("something new".as_bytes())?;
    drop(writer);
    // ...using commit2 so we can recover if the read-only attribute is set.
    match phazer.commit2() {
        Ok(()) => {
            println!("Success!  This was unexpected.  Windows up to 11 always failed.");
        }
        Err((e, p)) => {
            // If the error is anything except PermissionDenied then return it
            if e.kind() != ErrorKind::PermissionDenied {
                return Err(e.into());
            }
            // Clear the read-only attribute
            let _ = set_readonly(&target, false);
            // Try again
            p.commit()?;
        }
    }
    Ok(())
}

Trait Implementations§

Source§

impl<'cs> Drop for Phazer<'cs>

Source§

fn drop(&mut self)

drop removes the working file if it still exists (if the Phazer was not committed).

Auto Trait Implementations§

§

impl<'cs> !Freeze for Phazer<'cs>

§

impl<'cs> !RefUnwindSafe for Phazer<'cs>

§

impl<'cs> Send for Phazer<'cs>

§

impl<'cs> Sync for Phazer<'cs>

§

impl<'cs> Unpin for Phazer<'cs>

§

impl<'cs> !UnwindSafe for Phazer<'cs>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.