rocket::response

Struct Builder

Source
pub struct Builder<'r> { /* private fields */ }
Expand description

Builder for the Response type.

Building a Response can be a low-level ordeal; this structure presents a higher-level API that simplifies building Responses.

§Usage

Builder follows the builder pattern and is usually obtained by calling Response::build() on Response. Almost all methods take the current builder as a mutable reference and return the same mutable reference with field(s) modified in the Response being built. These method calls can be chained: build.a().b().

To finish building and retrieve the built Response, use the finalize() or ok() methods.

§Headers

When building a Response, headers can either be replaced or adjoined; the default behavior (using header(..)) is to replace. When a header is replaced, any existing values for headers with the same name are removed, and the new value is set. If no header exists, the header is simply added. On the other hand, when a header is adjoined, all existing values will remain, and the value of the adjoined header will be added to the set of existing values, if any. Adjoining maintains order: headers adjoined first will appear first in the Response.

§Joining and Merging

It is often necessary to combine multiple Responses in some way. The merge and join methods facilitate this. The merge method replaces all of the fields in self with those present in other. The join method sets any fields not set in self to the value in other. See their documentation for more details.

§Example

The following example builds a Response with:

  • Status: 418 I'm a teapot
  • Content-Type header: text/plain; charset=utf-8
  • X-Teapot-Make header: Rocket
  • X-Teapot-Model headers: Utopia, Series 1
  • Body: fixed-size string "Brewing the best coffee!"
use std::io::Cursor;
use rocket::response::Response;
use rocket::http::{Status, ContentType};

let body = "Brewing the best coffee!";
let response = Response::build()
    .status(Status::ImATeapot)
    .header(ContentType::Plain)
    .raw_header("X-Teapot-Make", "Rocket")
    .raw_header("X-Teapot-Model", "Utopia")
    .raw_header_adjoin("X-Teapot-Model", "Series 1")
    .sized_body(body.len(), Cursor::new(body))
    .finalize();

Implementations§

Source§

impl<'r> Builder<'r>

Source

pub fn new(base: Response<'r>) -> Builder<'r>

Creates a new Builder that will build on top of the base Response.

§Example
use rocket::response::{Builder, Response};

let builder = Builder::new(Response::new());
Source

pub fn status(&mut self, status: Status) -> &mut Builder<'r>

Sets the status of the Response being built to status.

§Example
use rocket::Response;
use rocket::http::Status;

let response = Response::build()
    .status(Status::NotFound)
    .finalize();
Source

pub fn header<'h: 'r, H>(&mut self, header: H) -> &mut Builder<'r>
where H: Into<Header<'h>>,

Adds header to the Response, replacing any header with the same name that already exists in the response. If multiple headers with the same name exist, they are all removed, and only the new header and value will remain.

The type of header can be any type that implements Into<Header>. See trait implementations.

§Example
use rocket::Response;
use rocket::http::ContentType;

let response = Response::build()
    .header(ContentType::JSON)
    .header(ContentType::HTML)
    .finalize();

assert_eq!(response.headers().get("Content-Type").count(), 1);
Source

pub fn header_adjoin<'h: 'r, H>(&mut self, header: H) -> &mut Builder<'r>
where H: Into<Header<'h>>,

Adds header to the Response by adjoining the header with any existing headers with the same name that already exist in the Response. This allows for multiple headers with the same name and potentially different values to be present in the Response.

The type of header can be any type that implements Into<Header>. See trait implementations.

§Example
use rocket::Response;
use rocket::http::{Header, Accept};

let response = Response::build()
    .header_adjoin(Header::new("Accept", "application/json"))
    .header_adjoin(Accept::XML)
    .finalize();

assert_eq!(response.headers().get("Accept").count(), 2);
Source

pub fn raw_header<'a, 'b, N, V>( &mut self, name: N, value: V, ) -> &mut Builder<'r>
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>, 'a: 'r, 'b: 'r,

Adds a custom header to the Response with the given name and value, replacing any header with the same name that already exists in the response. If multiple headers with the same name exist, they are all removed, and only the new header and value will remain.

§Example
use rocket::Response;

let response = Response::build()
    .raw_header("X-Custom", "first")
    .raw_header("X-Custom", "second")
    .finalize();

assert_eq!(response.headers().get("X-Custom").count(), 1);
Source

pub fn raw_header_adjoin<'a, 'b, N, V>( &mut self, name: N, value: V, ) -> &mut Builder<'r>
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>, 'a: 'r, 'b: 'r,

Adds custom header to the Response with the given name and value, adjoining the header with any existing headers with the same name that already exist in the Response. This allows for multiple headers with the same name and potentially different values to be present in the Response.

§Example
use rocket::Response;

let response = Response::build()
    .raw_header_adjoin("X-Custom", "first")
    .raw_header_adjoin("X-Custom", "second")
    .finalize();

assert_eq!(response.headers().get("X-Custom").count(), 2);
Source

pub fn sized_body<B, S>(&mut self, size: S, body: B) -> &mut Builder<'r>
where B: AsyncRead + AsyncSeek + Send + 'r, S: Into<Option<usize>>,

Sets the body of the Response to be the fixed-sized body with size size, which may be None. If size is None, the body’s size will be computed with calls to seek when the response is written out.

§Example
use std::io::Cursor;
use rocket::Response;

let body = "Hello, world!";
let response = Response::build()
    .sized_body(body.len(), Cursor::new(body))
    .finalize();
Source

pub fn streamed_body<B>(&mut self, body: B) -> &mut Builder<'r>
where B: AsyncRead + Send + 'r,

Sets the body of the Response to be the streamed body.

§Example
use std::io::Cursor;
use rocket::Response;

let response = Response::build()
    .streamed_body(Cursor::new("Hello, world!"))
    .finalize();
Source

pub fn upgrade<P, H>(&mut self, protocol: P, handler: H) -> &mut Builder<'r>
where P: Into<Uncased<'r>>, H: IoHandler + 'r,

Registers handler as the I/O handler for upgrade protocol protocol.

This is equivalent to Response::add_upgrade().

NOTE: Responses registering I/O handlers for upgraded protocols should not set the response status to 101 Switching Protocols, nor set the Connection or Upgrade headers. Rocket automatically sets these headers as needed. See Response#upgrading for details.

§Example
use std::pin::Pin;

use rocket::Response;
use rocket::data::{IoHandler, IoStream};
use rocket::tokio::io;

struct EchoHandler;

#[rocket::async_trait]
impl IoHandler for EchoHandler {
    async fn io(self: Box<Self>, io: IoStream) -> io::Result<()> {
        let (mut reader, mut writer) = io::split(io);
        io::copy(&mut reader, &mut writer).await?;
        Ok(())
    }
}

let response = Response::build()
    .upgrade("raw-echo", EchoHandler)
    .streamed_body(std::io::Cursor::new("We didn't upgrade!"))
    .finalize();
Source

pub fn max_chunk_size(&mut self, size: usize) -> &mut Builder<'r>

Sets the max chunk size of a body, if any, to size.

See Response::set_max_chunk_size() for notes.

§Example
use std::io::Cursor;
use rocket::Response;

let response = Response::build()
    .streamed_body(Cursor::new("Hello, world!"))
    .max_chunk_size(3072)
    .finalize();
Source

pub fn merge(&mut self, other: Response<'r>) -> &mut Builder<'r>

Merges the other Response into self by setting any fields in self to the corresponding value in other if they are set in other. Fields in self are unchanged if they are not set in other. If a header is set in both self and other, the values in other are kept. Headers set only in self remain.

§Example
use rocket::Response;
use rocket::http::{Status, ContentType};

let base = Response::build()
    .status(Status::NotFound)
    .header(ContentType::HTML)
    .raw_header("X-Custom", "value 1")
    .finalize();

let response = Response::build()
    .status(Status::ImATeapot)
    .raw_header("X-Custom", "value 2")
    .raw_header_adjoin("X-Custom", "value 3")
    .merge(base)
    .finalize();

assert_eq!(response.status(), Status::NotFound);

let ctype: Vec<_> = response.headers().get("Content-Type").collect();
assert_eq!(ctype, vec![ContentType::HTML.to_string()]);

let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
assert_eq!(custom_values, vec!["value 1"]);
Source

pub fn join(&mut self, other: Response<'r>) -> &mut Builder<'r>

Joins the other Response into self by setting any fields in self to the corresponding value in other if they are set in self. Fields in self are unchanged if they are already set. If a header is set in both self and other, the values are adjoined, with the values in self coming first. Headers only in self or other are set in self.

§Example
use rocket::Response;
use rocket::http::{Status, ContentType};

let other = Response::build()
    .status(Status::NotFound)
    .header(ContentType::HTML)
    .raw_header("X-Custom", "value 1")
    .finalize();

let response = Response::build()
    .status(Status::ImATeapot)
    .raw_header("X-Custom", "value 2")
    .raw_header_adjoin("X-Custom", "value 3")
    .join(other)
    .finalize();

assert_eq!(response.status(), Status::ImATeapot);

let ctype: Vec<_> = response.headers().get("Content-Type").collect();
assert_eq!(ctype, vec![ContentType::HTML.to_string()]);

let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
assert_eq!(custom_values, vec!["value 2", "value 3", "value 1"]);
Source

pub fn finalize(&mut self) -> Response<'r>

Return the Response structure that was being built by this builder. After calling this method, self is cleared and must be rebuilt as if from new().

§Example
use std::io::Cursor;

use rocket::Response;
use rocket::http::Status;

let body = "Brewing the best coffee!";
let response = Response::build()
    .status(Status::ImATeapot)
    .sized_body(body.len(), Cursor::new(body))
    .raw_header("X-Custom", "value 2")
    .finalize();
Source

pub fn ok<E>(&mut self) -> Result<Response<'r>, E>

Retrieve the built Response wrapped in Ok. After calling this method, self is cleared and must be rebuilt as if from new().

§Example
use rocket::Response;

let response: Result<Response, ()> = Response::build()
    // build the response
    .ok();

assert!(response.is_ok());

Auto Trait Implementations§

§

impl<'r> Freeze for Builder<'r>

§

impl<'r> !RefUnwindSafe for Builder<'r>

§

impl<'r> Send for Builder<'r>

§

impl<'r> !Sync for Builder<'r>

§

impl<'r> Unpin for Builder<'r>

§

impl<'r> !UnwindSafe for Builder<'r>

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> AsAny for T
where T: Any,

Source§

fn as_any_ref(&self) -> &(dyn Any + 'static)

Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

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> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

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

Source§

fn fg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the foreground set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like red() and green(), which have the same functionality but are pithier.

§Example

Set foreground color to white using fg():

use yansi::{Paint, Color};

painted.fg(Color::White);

Set foreground color to white using white().

use yansi::Paint;

painted.white();
Source§

fn primary(&self) -> Painted<&T>

Returns self with the fg() set to Color::Primary.

§Example
println!("{}", value.primary());
Source§

fn fixed(&self, color: u8) -> Painted<&T>

Returns self with the fg() set to Color::Fixed.

§Example
println!("{}", value.fixed(color));
Source§

fn rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the fg() set to Color::Rgb.

§Example
println!("{}", value.rgb(r, g, b));
Source§

fn black(&self) -> Painted<&T>

Returns self with the fg() set to Color::Black.

§Example
println!("{}", value.black());
Source§

fn red(&self) -> Painted<&T>

Returns self with the fg() set to Color::Red.

§Example
println!("{}", value.red());
Source§

fn green(&self) -> Painted<&T>

Returns self with the fg() set to Color::Green.

§Example
println!("{}", value.green());
Source§

fn yellow(&self) -> Painted<&T>

Returns self with the fg() set to Color::Yellow.

§Example
println!("{}", value.yellow());
Source§

fn blue(&self) -> Painted<&T>

Returns self with the fg() set to Color::Blue.

§Example
println!("{}", value.blue());
Source§

fn magenta(&self) -> Painted<&T>

Returns self with the fg() set to Color::Magenta.

§Example
println!("{}", value.magenta());
Source§

fn cyan(&self) -> Painted<&T>

Returns self with the fg() set to Color::Cyan.

§Example
println!("{}", value.cyan());
Source§

fn white(&self) -> Painted<&T>

Returns self with the fg() set to Color::White.

§Example
println!("{}", value.white());
Source§

fn bright_black(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightBlack.

§Example
println!("{}", value.bright_black());
Source§

fn bright_red(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightRed.

§Example
println!("{}", value.bright_red());
Source§

fn bright_green(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightGreen.

§Example
println!("{}", value.bright_green());
Source§

fn bright_yellow(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightYellow.

§Example
println!("{}", value.bright_yellow());
Source§

fn bright_blue(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightBlue.

§Example
println!("{}", value.bright_blue());
Source§

fn bright_magenta(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightMagenta.

§Example
println!("{}", value.bright_magenta());
Source§

fn bright_cyan(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightCyan.

§Example
println!("{}", value.bright_cyan());
Source§

fn bright_white(&self) -> Painted<&T>

Returns self with the fg() set to Color::BrightWhite.

§Example
println!("{}", value.bright_white());
Source§

fn bg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the background set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like on_red() and on_green(), which have the same functionality but are pithier.

§Example

Set background color to red using fg():

use yansi::{Paint, Color};

painted.bg(Color::Red);

Set background color to red using on_red().

use yansi::Paint;

painted.on_red();
Source§

fn on_primary(&self) -> Painted<&T>

Returns self with the bg() set to Color::Primary.

§Example
println!("{}", value.on_primary());
Source§

fn on_fixed(&self, color: u8) -> Painted<&T>

Returns self with the bg() set to Color::Fixed.

§Example
println!("{}", value.on_fixed(color));
Source§

fn on_rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the bg() set to Color::Rgb.

§Example
println!("{}", value.on_rgb(r, g, b));
Source§

fn on_black(&self) -> Painted<&T>

Returns self with the bg() set to Color::Black.

§Example
println!("{}", value.on_black());
Source§

fn on_red(&self) -> Painted<&T>

Returns self with the bg() set to Color::Red.

§Example
println!("{}", value.on_red());
Source§

fn on_green(&self) -> Painted<&T>

Returns self with the bg() set to Color::Green.

§Example
println!("{}", value.on_green());
Source§

fn on_yellow(&self) -> Painted<&T>

Returns self with the bg() set to Color::Yellow.

§Example
println!("{}", value.on_yellow());
Source§

fn on_blue(&self) -> Painted<&T>

Returns self with the bg() set to Color::Blue.

§Example
println!("{}", value.on_blue());
Source§

fn on_magenta(&self) -> Painted<&T>

Returns self with the bg() set to Color::Magenta.

§Example
println!("{}", value.on_magenta());
Source§

fn on_cyan(&self) -> Painted<&T>

Returns self with the bg() set to Color::Cyan.

§Example
println!("{}", value.on_cyan());
Source§

fn on_white(&self) -> Painted<&T>

Returns self with the bg() set to Color::White.

§Example
println!("{}", value.on_white());
Source§

fn on_bright_black(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightBlack.

§Example
println!("{}", value.on_bright_black());
Source§

fn on_bright_red(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightRed.

§Example
println!("{}", value.on_bright_red());
Source§

fn on_bright_green(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightGreen.

§Example
println!("{}", value.on_bright_green());
Source§

fn on_bright_yellow(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightYellow.

§Example
println!("{}", value.on_bright_yellow());
Source§

fn on_bright_blue(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightBlue.

§Example
println!("{}", value.on_bright_blue());
Source§

fn on_bright_magenta(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightMagenta.

§Example
println!("{}", value.on_bright_magenta());
Source§

fn on_bright_cyan(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightCyan.

§Example
println!("{}", value.on_bright_cyan());
Source§

fn on_bright_white(&self) -> Painted<&T>

Returns self with the bg() set to Color::BrightWhite.

§Example
println!("{}", value.on_bright_white());
Source§

fn attr(&self, value: Attribute) -> Painted<&T>

Enables the styling Attribute value.

This method should be used rarely. Instead, prefer to use attribute-specific builder methods like bold() and underline(), which have the same functionality but are pithier.

§Example

Make text bold using attr():

use yansi::{Paint, Attribute};

painted.attr(Attribute::Bold);

Make text bold using using bold().

use yansi::Paint;

painted.bold();
Source§

fn bold(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Bold.

§Example
println!("{}", value.bold());
Source§

fn dim(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Dim.

§Example
println!("{}", value.dim());
Source§

fn italic(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Italic.

§Example
println!("{}", value.italic());
Source§

fn underline(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Underline.

§Example
println!("{}", value.underline());

Returns self with the attr() set to Attribute::Blink.

§Example
println!("{}", value.blink());

Returns self with the attr() set to Attribute::RapidBlink.

§Example
println!("{}", value.rapid_blink());
Source§

fn invert(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Invert.

§Example
println!("{}", value.invert());
Source§

fn conceal(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Conceal.

§Example
println!("{}", value.conceal());
Source§

fn strike(&self) -> Painted<&T>

Returns self with the attr() set to Attribute::Strike.

§Example
println!("{}", value.strike());
Source§

fn quirk(&self, value: Quirk) -> Painted<&T>

Enables the yansi Quirk value.

This method should be used rarely. Instead, prefer to use quirk-specific builder methods like mask() and wrap(), which have the same functionality but are pithier.

§Example

Enable wrapping using .quirk():

use yansi::{Paint, Quirk};

painted.quirk(Quirk::Wrap);

Enable wrapping using wrap().

use yansi::Paint;

painted.wrap();
Source§

fn mask(&self) -> Painted<&T>

Returns self with the quirk() set to Quirk::Mask.

§Example
println!("{}", value.mask());
Source§

fn wrap(&self) -> Painted<&T>

Returns self with the quirk() set to Quirk::Wrap.

§Example
println!("{}", value.wrap());
Source§

fn linger(&self) -> Painted<&T>

Returns self with the quirk() set to Quirk::Linger.

§Example
println!("{}", value.linger());
Source§

fn clear(&self) -> Painted<&T>

👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear(). The clear() method will be removed in a future release.

Returns self with the quirk() set to Quirk::Clear.

§Example
println!("{}", value.clear());
Source§

fn resetting(&self) -> Painted<&T>

Returns self with the quirk() set to Quirk::Resetting.

§Example
println!("{}", value.resetting());
Source§

fn bright(&self) -> Painted<&T>

Returns self with the quirk() set to Quirk::Bright.

§Example
println!("{}", value.bright());
Source§

fn on_bright(&self) -> Painted<&T>

Returns self with the quirk() set to Quirk::OnBright.

§Example
println!("{}", value.on_bright());
Source§

fn whenever(&self, value: Condition) -> Painted<&T>

Conditionally enable styling based on whether the Condition value applies. Replaces any previous condition.

See the crate level docs for more details.

§Example

Enable styling painted only when both stdout and stderr are TTYs:

use yansi::{Paint, Condition};

painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);
Source§

fn new(self) -> Painted<Self>
where Self: Sized,

Create a new Painted with a default Style. Read more
Source§

fn paint<S>(&self, style: S) -> Painted<&Self>
where S: Into<Style>,

Apply a style wholesale to self. Any previous style is replaced. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

impl<T, U> Upcast<T> for U
where T: UpcastFrom<U>,

Source§

fn upcast(self) -> T

Source§

impl<T, B> UpcastFrom<Counter<T, B>> for T

Source§

fn upcast_from(value: Counter<T, B>) -> T

Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more