├── examples ├── .tokeignore ├── update │ ├── Cargo.toml │ └── update-examples.rs ├── input.rs ├── output.rustfmt.rs ├── output.rustc.rs └── output.prettyplease.rs ├── .gitignore ├── .gitattributes ├── src ├── lifetime.rs ├── file.rs ├── iter.rs ├── lit.rs ├── ring.rs ├── convenience.rs ├── data.rs ├── token.rs ├── stmt.rs ├── path.rs ├── pat.rs ├── ty.rs ├── attr.rs ├── generics.rs ├── algorithm.rs └── lib.rs ├── cargo-expand └── update │ ├── Cargo.toml │ └── update.rs ├── Cargo.toml ├── LICENSE-MIT ├── .github └── workflows │ └── ci.yml ├── LICENSE-APACHE └── README.md /examples/.tokeignore: -------------------------------------------------------------------------------- 1 | *.rs 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | cargo-expand/*.rs linguist-generated 2 | examples/*.rs linguist-generated 3 | -------------------------------------------------------------------------------- /src/lifetime.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use syn::Lifetime; 3 | 4 | impl Printer { 5 | pub fn lifetime(&mut self, lifetime: &Lifetime) { 6 | self.word("'"); 7 | self.ident(&lifetime.ident); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cargo-expand/update/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prettyplease-update" 3 | version = "0.0.0" 4 | authors = ["David Tolnay "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [[bin]] 9 | name = "prettyplease-update" 10 | path = "update.rs" 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | prettyplease = { path = "../../", features = ["verbatim"] } 15 | syn = { version = "1.0", default-features = false, features = ["parsing", "printing"] } 16 | -------------------------------------------------------------------------------- /src/file.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use syn::File; 3 | 4 | impl Printer { 5 | pub fn file(&mut self, file: &File) { 6 | self.cbox(0); 7 | if let Some(shebang) = &file.shebang { 8 | self.word(shebang.clone()); 9 | self.hardbreak(); 10 | } 11 | self.inner_attrs(&file.attrs); 12 | for item in &file.items { 13 | self.item(item); 14 | } 15 | self.end(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/update/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prettyplease-update-examples" 3 | version = "0.0.0" 4 | authors = ["David Tolnay "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [[bin]] 9 | name = "prettyplease-update-examples" 10 | path = "update-examples.rs" 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | prettplease = { path = "../../" } 15 | quote = { version = "1.0", default-features = false } 16 | syn = { version = "1.0", default-features = false, features = ["parsing", "printing"] } 17 | -------------------------------------------------------------------------------- /cargo-expand/update/update.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::ffi::OsStr; 3 | use std::fs; 4 | use std::path::Path; 5 | 6 | fn main() -> Result<()> { 7 | let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); 8 | let cargo_expand_dir = manifest_dir.join(".."); 9 | 10 | for entry in fs::read_dir(cargo_expand_dir)? { 11 | let entry = entry?; 12 | let file_type = entry.file_type()?; 13 | if !file_type.is_file() { 14 | continue; 15 | } 16 | 17 | let path = entry.path(); 18 | if path.extension() != Some(OsStr::new("rs")) { 19 | continue; 20 | } 21 | 22 | let input_contents = fs::read_to_string(&path)?; 23 | let syntax_tree = syn::parse_file(&input_contents)?; 24 | let string = prettyplease::unparse(&syntax_tree); 25 | fs::write(&path, string)?; 26 | } 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prettier-please" 3 | version = "0.2.0" 4 | authors = ["David Tolnay ", "Jon Kelley"] 5 | edition = "2021" 6 | rust-version = "1.56" 7 | license = "MIT OR Apache-2.0" 8 | description = "A minimal `syn` syntax tree pretty-printer" 9 | repository = "https://github.com/dtolnay/prettyplease" 10 | documentation = "https://docs.rs/prettyplease" 11 | readme = "README.md" 12 | links = "prettyplease01" 13 | autoexamples = false 14 | exclude = ["cargo-expand"] 15 | 16 | [features] 17 | verbatim = ["syn/parsing"] 18 | 19 | [dependencies] 20 | proc-macro2 = { version = "1.0.63", default-features = false } 21 | syn = { version = "2.0.23", default-features = false, features = ["full"] } 22 | 23 | [dev-dependencies] 24 | syn = { version = "2.0.23", default-features = false, features = ["parsing"] } 25 | 26 | [lib] 27 | doc-scrape-examples = false 28 | 29 | [package.metadata.docs.rs] 30 | targets = ["x86_64-unknown-linux-gnu"] 31 | 32 | [package.metadata.playground] 33 | features = ["verbatim"] 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | use std::iter::Peekable; 2 | use std::ops::Deref; 3 | 4 | pub struct Delimited { 5 | is_first: bool, 6 | iter: Peekable, 7 | } 8 | 9 | pub trait IterDelimited: Iterator + Sized { 10 | fn delimited(self) -> Delimited { 11 | Delimited { 12 | is_first: true, 13 | iter: self.peekable(), 14 | } 15 | } 16 | } 17 | 18 | impl IterDelimited for I {} 19 | 20 | pub struct IteratorItem { 21 | value: T, 22 | pub is_first: bool, 23 | pub is_last: bool, 24 | } 25 | 26 | impl Iterator for Delimited { 27 | type Item = IteratorItem; 28 | 29 | fn next(&mut self) -> Option { 30 | let item = IteratorItem { 31 | value: self.iter.next()?, 32 | is_first: self.is_first, 33 | is_last: self.iter.peek().is_none(), 34 | }; 35 | self.is_first = false; 36 | Some(item) 37 | } 38 | } 39 | 40 | impl Deref for IteratorItem { 41 | type Target = T; 42 | 43 | fn deref(&self) -> &Self::Target { 44 | &self.value 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/lit.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use proc_macro2::Literal; 3 | use syn::{Lit, LitBool, LitByte, LitByteStr, LitChar, LitFloat, LitInt, LitStr}; 4 | 5 | impl Printer { 6 | pub fn lit(&mut self, lit: &Lit) { 7 | match lit { 8 | Lit::Str(lit) => self.lit_str(lit), 9 | Lit::ByteStr(lit) => self.lit_byte_str(lit), 10 | Lit::Byte(lit) => self.lit_byte(lit), 11 | Lit::Char(lit) => self.lit_char(lit), 12 | Lit::Int(lit) => self.lit_int(lit), 13 | Lit::Float(lit) => self.lit_float(lit), 14 | Lit::Bool(lit) => self.lit_bool(lit), 15 | Lit::Verbatim(lit) => self.lit_verbatim(lit), 16 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 17 | _ => unimplemented!("unknown Lit"), 18 | } 19 | } 20 | 21 | pub fn lit_str(&mut self, lit: &LitStr) { 22 | self.word(lit.token().to_string()); 23 | } 24 | 25 | fn lit_byte_str(&mut self, lit: &LitByteStr) { 26 | self.word(lit.token().to_string()); 27 | } 28 | 29 | fn lit_byte(&mut self, lit: &LitByte) { 30 | self.word(lit.token().to_string()); 31 | } 32 | 33 | fn lit_char(&mut self, lit: &LitChar) { 34 | self.word(lit.token().to_string()); 35 | } 36 | 37 | fn lit_int(&mut self, lit: &LitInt) { 38 | self.word(lit.token().to_string()); 39 | } 40 | 41 | fn lit_float(&mut self, lit: &LitFloat) { 42 | self.word(lit.token().to_string()); 43 | } 44 | 45 | fn lit_bool(&mut self, lit: &LitBool) { 46 | self.word(if lit.value { "true" } else { "false" }); 47 | } 48 | 49 | fn lit_verbatim(&mut self, token: &Literal) { 50 | self.word(token.to_string()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: [cron: "40 1 * * *"] 7 | 8 | env: 9 | RUSTFLAGS: -Dwarnings 10 | 11 | jobs: 12 | test: 13 | name: Rust ${{matrix.rust}} 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | rust: [nightly, beta, stable, 1.56.0] 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: dtolnay/rust-toolchain@master 22 | with: 23 | toolchain: ${{matrix.rust}} 24 | - run: cargo check 25 | - run: cargo check --features verbatim 26 | - run: cargo test 27 | env: 28 | RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.rust == 'nightly' && '--cfg exhaustive' || ''}} 29 | 30 | examples: 31 | name: Examples 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v3 35 | - uses: dtolnay/rust-toolchain@nightly 36 | with: 37 | components: rustc-dev, rustfmt 38 | - run: cargo run --manifest-path examples/update/Cargo.toml 39 | - run: git diff --exit-code 40 | - run: cargo run --manifest-path cargo-expand/update/Cargo.toml 41 | - run: git diff --exit-code 42 | 43 | clippy: 44 | name: Clippy 45 | runs-on: ubuntu-latest 46 | if: github.event_name != 'pull_request' 47 | steps: 48 | - uses: actions/checkout@v3 49 | - uses: dtolnay/rust-toolchain@clippy 50 | - run: cargo clippy --features verbatim -- -Dclippy::all -Dclippy::pedantic 51 | 52 | outdated: 53 | name: Outdated 54 | runs-on: ubuntu-latest 55 | if: github.event_name != 'pull_request' 56 | steps: 57 | - uses: actions/checkout@v3 58 | - uses: dtolnay/install@cargo-outdated 59 | - run: cargo outdated --workspace --exit-code 1 60 | -------------------------------------------------------------------------------- /src/ring.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::ops::{Index, IndexMut}; 3 | 4 | pub struct RingBuffer { 5 | data: VecDeque, 6 | // Abstract index of data[0] in infinitely sized queue 7 | offset: usize, 8 | } 9 | 10 | impl RingBuffer { 11 | pub fn new() -> Self { 12 | RingBuffer { 13 | data: VecDeque::new(), 14 | offset: 0, 15 | } 16 | } 17 | 18 | pub fn is_empty(&self) -> bool { 19 | self.data.is_empty() 20 | } 21 | 22 | pub fn len(&self) -> usize { 23 | self.data.len() 24 | } 25 | 26 | pub fn push(&mut self, value: T) -> usize { 27 | let index = self.offset + self.data.len(); 28 | self.data.push_back(value); 29 | index 30 | } 31 | 32 | pub fn clear(&mut self) { 33 | self.data.clear(); 34 | } 35 | 36 | pub fn index_of_first(&self) -> usize { 37 | self.offset 38 | } 39 | 40 | pub fn first(&self) -> &T { 41 | &self.data[0] 42 | } 43 | 44 | pub fn first_mut(&mut self) -> &mut T { 45 | &mut self.data[0] 46 | } 47 | 48 | pub fn pop_first(&mut self) -> T { 49 | self.offset += 1; 50 | self.data.pop_front().unwrap() 51 | } 52 | 53 | pub fn last(&self) -> &T { 54 | self.data.back().unwrap() 55 | } 56 | 57 | pub fn last_mut(&mut self) -> &mut T { 58 | self.data.back_mut().unwrap() 59 | } 60 | 61 | pub fn second_last(&self) -> &T { 62 | &self.data[self.data.len() - 2] 63 | } 64 | 65 | pub fn pop_last(&mut self) { 66 | self.data.pop_back().unwrap(); 67 | } 68 | } 69 | 70 | impl Index for RingBuffer { 71 | type Output = T; 72 | fn index(&self, index: usize) -> &Self::Output { 73 | &self.data[index.checked_sub(self.offset).unwrap()] 74 | } 75 | } 76 | 77 | impl IndexMut for RingBuffer { 78 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 79 | &mut self.data[index.checked_sub(self.offset).unwrap()] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/update/update-examples.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | 3 | extern crate rustc_ast_pretty; 4 | extern crate rustc_parse; 5 | extern crate rustc_session; 6 | extern crate rustc_span; 7 | 8 | use anyhow::Result; 9 | use quote::quote; 10 | use rustc_session::parse::ParseSess; 11 | use rustc_span::edition::Edition::Edition2021; 12 | use rustc_span::source_map::FilePathMapping; 13 | use std::fs::{self, File}; 14 | use std::path::Path; 15 | use std::process::{Command, Stdio}; 16 | 17 | fn main() -> Result<()> { 18 | let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); 19 | 20 | // Read and parse input.rs 21 | let input_path = manifest_dir.join("..").join("input.rs"); 22 | let input_contents = fs::read_to_string(&input_path)?; 23 | let syntax_tree = syn::parse_file(&input_contents)?; 24 | 25 | // Write input.rs 26 | let tokens = quote!(#syntax_tree); 27 | let mut string = tokens.to_string(); 28 | string.push('\n'); 29 | fs::write(&input_path, string)?; 30 | 31 | // Write output.prettyplease.rs 32 | let output_path = manifest_dir.join("..").join("output.prettyplease.rs"); 33 | let string = prettyplease::unparse(&syntax_tree); 34 | fs::write(&output_path, string)?; 35 | 36 | // Write output.rustc.rs 37 | let output_path = manifest_dir.join("..").join("output.rustc.rs"); 38 | let mut string = rustc_span::create_session_globals_then(Edition2021, || { 39 | let sess = ParseSess::new(FilePathMapping::new(Vec::new())); 40 | let krate = rustc_parse::parse_crate_from_file(&input_path, &sess).unwrap(); 41 | rustc_ast_pretty::pprust::crate_to_string_for_macros(&krate) 42 | }); 43 | string.push('\n'); 44 | fs::write(&output_path, string)?; 45 | 46 | // Write output.rustfmt.rs 47 | let output_path = manifest_dir.join("..").join("output.rustfmt.rs"); 48 | let output_file = File::create(output_path)?; 49 | Command::new("rustfmt") 50 | .arg("--edition=2021") 51 | .arg("--config=reorder_imports=false") 52 | .arg("--config=normalize_doc_attributes=true") 53 | .arg("--emit=stdout") 54 | .arg("--quiet") 55 | .arg("--unstable-features") 56 | .arg("--skip-children") 57 | .arg(&input_path) 58 | .stdin(Stdio::null()) 59 | .stdout(output_file) 60 | .stderr(Stdio::inherit()) 61 | .spawn()? 62 | .wait()?; 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /src/convenience.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::{self, BeginToken, BreakToken, Breaks, Printer}; 2 | use std::borrow::Cow; 3 | 4 | impl Printer { 5 | pub fn ibox(&mut self, indent: isize) { 6 | self.scan_begin(BeginToken { 7 | offset: indent, 8 | breaks: Breaks::Inconsistent, 9 | }); 10 | } 11 | 12 | pub fn cbox(&mut self, indent: isize) { 13 | self.scan_begin(BeginToken { 14 | offset: indent, 15 | breaks: Breaks::Consistent, 16 | }); 17 | } 18 | 19 | pub fn end(&mut self) { 20 | self.scan_end(); 21 | } 22 | 23 | pub fn word>>(&mut self, wrd: S) { 24 | let s = wrd.into(); 25 | self.scan_string(s); 26 | } 27 | 28 | fn spaces(&mut self, n: usize) { 29 | self.scan_break(BreakToken { 30 | blank_space: n, 31 | ..BreakToken::default() 32 | }); 33 | } 34 | 35 | pub fn zerobreak(&mut self) { 36 | self.spaces(0); 37 | } 38 | 39 | pub fn space(&mut self) { 40 | self.spaces(1); 41 | } 42 | 43 | pub fn nbsp(&mut self) { 44 | self.word(" "); 45 | } 46 | 47 | pub fn hardbreak(&mut self) { 48 | self.spaces(algorithm::SIZE_INFINITY as usize); 49 | } 50 | 51 | pub fn space_if_nonempty(&mut self) { 52 | self.scan_break(BreakToken { 53 | blank_space: 1, 54 | if_nonempty: true, 55 | ..BreakToken::default() 56 | }); 57 | } 58 | 59 | pub fn hardbreak_if_nonempty(&mut self) { 60 | self.scan_break(BreakToken { 61 | blank_space: algorithm::SIZE_INFINITY as usize, 62 | if_nonempty: true, 63 | ..BreakToken::default() 64 | }); 65 | } 66 | 67 | pub fn trailing_comma(&mut self, is_last: bool) { 68 | if is_last { 69 | self.scan_break(BreakToken { 70 | pre_break: Some(','), 71 | ..BreakToken::default() 72 | }); 73 | } else { 74 | self.word(","); 75 | self.space(); 76 | } 77 | } 78 | 79 | pub fn trailing_comma_or_space(&mut self, is_last: bool) { 80 | if is_last { 81 | self.scan_break(BreakToken { 82 | blank_space: 1, 83 | pre_break: Some(','), 84 | ..BreakToken::default() 85 | }); 86 | } else { 87 | self.word(","); 88 | self.space(); 89 | } 90 | } 91 | 92 | pub fn neverbreak(&mut self) { 93 | self.scan_break(BreakToken { 94 | never_break: true, 95 | ..BreakToken::default() 96 | }); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/data.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::iter::IterDelimited; 3 | use crate::path::PathKind; 4 | use crate::INDENT; 5 | use syn::{Field, Fields, FieldsUnnamed, Variant, VisRestricted, Visibility}; 6 | 7 | impl Printer { 8 | pub fn variant(&mut self, variant: &Variant) { 9 | self.outer_attrs(&variant.attrs); 10 | self.ident(&variant.ident); 11 | match &variant.fields { 12 | Fields::Named(fields) => { 13 | self.nbsp(); 14 | self.word("{"); 15 | self.cbox(INDENT); 16 | self.space(); 17 | for field in fields.named.iter().delimited() { 18 | self.field(&field); 19 | self.trailing_comma_or_space(field.is_last); 20 | } 21 | self.offset(-INDENT); 22 | self.end(); 23 | self.word("}"); 24 | } 25 | Fields::Unnamed(fields) => { 26 | self.cbox(INDENT); 27 | self.fields_unnamed(fields); 28 | self.end(); 29 | } 30 | Fields::Unit => {} 31 | } 32 | if let Some((_eq_token, discriminant)) = &variant.discriminant { 33 | self.word(" = "); 34 | self.expr(discriminant); 35 | } 36 | } 37 | 38 | pub fn fields_unnamed(&mut self, fields: &FieldsUnnamed) { 39 | self.word("("); 40 | self.zerobreak(); 41 | for field in fields.unnamed.iter().delimited() { 42 | self.field(&field); 43 | self.trailing_comma(field.is_last); 44 | } 45 | self.offset(-INDENT); 46 | self.word(")"); 47 | } 48 | 49 | pub fn field(&mut self, field: &Field) { 50 | self.outer_attrs(&field.attrs); 51 | self.visibility(&field.vis); 52 | if let Some(ident) = &field.ident { 53 | self.ident(ident); 54 | self.word(": "); 55 | } 56 | self.ty(&field.ty); 57 | } 58 | 59 | pub fn visibility(&mut self, vis: &Visibility) { 60 | match vis { 61 | Visibility::Public(_) => self.word("pub "), 62 | Visibility::Restricted(vis) => self.vis_restricted(vis), 63 | Visibility::Inherited => {} 64 | } 65 | } 66 | 67 | fn vis_restricted(&mut self, vis: &VisRestricted) { 68 | self.word("pub("); 69 | let omit_in = vis.path.get_ident().map_or(false, |ident| { 70 | matches!(ident.to_string().as_str(), "self" | "super" | "crate") 71 | }); 72 | if !omit_in { 73 | self.word("in "); 74 | } 75 | self.path(&vis.path, PathKind::Simple); 76 | self.word(") "); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use proc_macro2::{Delimiter, Ident, Literal, Spacing, TokenStream, TokenTree}; 3 | 4 | impl Printer { 5 | pub fn single_token(&mut self, token: Token, group_contents: fn(&mut Self, TokenStream)) { 6 | match token { 7 | Token::Group(delimiter, stream) => self.token_group(delimiter, stream, group_contents), 8 | Token::Ident(ident) => self.ident(&ident), 9 | Token::Punct(ch, _spacing) => self.token_punct(ch), 10 | Token::Literal(literal) => self.token_literal(&literal), 11 | } 12 | } 13 | 14 | fn token_group( 15 | &mut self, 16 | delimiter: Delimiter, 17 | stream: TokenStream, 18 | group_contents: fn(&mut Self, TokenStream), 19 | ) { 20 | self.delimiter_open(delimiter); 21 | if !stream.is_empty() { 22 | if delimiter == Delimiter::Brace { 23 | self.space(); 24 | } 25 | group_contents(self, stream); 26 | if delimiter == Delimiter::Brace { 27 | self.space(); 28 | } 29 | } 30 | self.delimiter_close(delimiter); 31 | } 32 | 33 | pub fn ident(&mut self, ident: &Ident) { 34 | self.word(ident.to_string()); 35 | } 36 | 37 | pub fn token_punct(&mut self, ch: char) { 38 | self.word(ch.to_string()); 39 | } 40 | 41 | pub fn token_literal(&mut self, literal: &Literal) { 42 | self.word(literal.to_string()); 43 | } 44 | 45 | pub fn delimiter_open(&mut self, delimiter: Delimiter) { 46 | self.word(match delimiter { 47 | Delimiter::Parenthesis => "(", 48 | Delimiter::Brace => "{", 49 | Delimiter::Bracket => "[", 50 | Delimiter::None => return, 51 | }); 52 | } 53 | 54 | pub fn delimiter_close(&mut self, delimiter: Delimiter) { 55 | self.word(match delimiter { 56 | Delimiter::Parenthesis => ")", 57 | Delimiter::Brace => "}", 58 | Delimiter::Bracket => "]", 59 | Delimiter::None => return, 60 | }); 61 | } 62 | } 63 | 64 | pub enum Token { 65 | Group(Delimiter, TokenStream), 66 | Ident(Ident), 67 | Punct(char, Spacing), 68 | Literal(Literal), 69 | } 70 | 71 | impl From for Token { 72 | fn from(tt: TokenTree) -> Self { 73 | match tt { 74 | TokenTree::Group(group) => Token::Group(group.delimiter(), group.stream()), 75 | TokenTree::Ident(ident) => Token::Ident(ident), 76 | TokenTree::Punct(punct) => Token::Punct(punct.as_char(), punct.spacing()), 77 | TokenTree::Literal(literal) => Token::Literal(literal), 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/stmt.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::INDENT; 3 | use syn::{BinOp, Expr, Stmt}; 4 | 5 | impl Printer { 6 | pub fn stmt(&mut self, stmt: &Stmt) { 7 | match stmt { 8 | Stmt::Local(local) => { 9 | self.outer_attrs(&local.attrs); 10 | self.ibox(0); 11 | self.word("let "); 12 | self.pat(&local.pat); 13 | if let Some(local_init) = &local.init { 14 | self.word(" = "); 15 | self.neverbreak(); 16 | self.expr(&local_init.expr); 17 | if let Some((_else, diverge)) = &local_init.diverge { 18 | self.word(" else "); 19 | if let Expr::Block(expr) = diverge.as_ref() { 20 | self.small_block(&expr.block, &[]); 21 | } else { 22 | self.word("{"); 23 | self.space(); 24 | self.ibox(INDENT); 25 | self.expr(diverge); 26 | self.end(); 27 | self.space(); 28 | self.offset(-INDENT); 29 | self.word("}"); 30 | } 31 | } 32 | } 33 | self.word(";"); 34 | self.end(); 35 | self.hardbreak(); 36 | } 37 | Stmt::Item(item) => self.item(item), 38 | Stmt::Expr(expr, None) => { 39 | if break_after(expr) { 40 | self.ibox(0); 41 | self.expr_beginning_of_line(expr, true); 42 | if add_semi(expr) { 43 | self.word(";"); 44 | } 45 | self.end(); 46 | self.hardbreak(); 47 | } else { 48 | self.expr_beginning_of_line(expr, true); 49 | } 50 | } 51 | Stmt::Expr(expr, Some(_semi)) => { 52 | if let Expr::Verbatim(tokens) = expr { 53 | if tokens.is_empty() { 54 | return; 55 | } 56 | } 57 | self.ibox(0); 58 | self.expr_beginning_of_line(expr, true); 59 | if !remove_semi(expr) { 60 | self.word(";"); 61 | } 62 | self.end(); 63 | self.hardbreak(); 64 | } 65 | Stmt::Macro(stmt) => { 66 | self.outer_attrs(&stmt.attrs); 67 | let semicolon = true; 68 | self.mac(&stmt.mac, None, semicolon); 69 | self.hardbreak(); 70 | } 71 | } 72 | } 73 | } 74 | 75 | pub fn add_semi(expr: &Expr) -> bool { 76 | match expr { 77 | Expr::Assign(_) | Expr::Break(_) | Expr::Continue(_) | Expr::Return(_) | Expr::Yield(_) => { 78 | true 79 | } 80 | Expr::Binary(expr) => match expr.op { 81 | BinOp::AddAssign(_) 82 | | BinOp::SubAssign(_) 83 | | BinOp::MulAssign(_) 84 | | BinOp::DivAssign(_) 85 | | BinOp::RemAssign(_) 86 | | BinOp::BitXorAssign(_) 87 | | BinOp::BitAndAssign(_) 88 | | BinOp::BitOrAssign(_) 89 | | BinOp::ShlAssign(_) 90 | | BinOp::ShrAssign(_) => true, 91 | BinOp::Add(_) 92 | | BinOp::Sub(_) 93 | | BinOp::Mul(_) 94 | | BinOp::Div(_) 95 | | BinOp::Rem(_) 96 | | BinOp::And(_) 97 | | BinOp::Or(_) 98 | | BinOp::BitXor(_) 99 | | BinOp::BitAnd(_) 100 | | BinOp::BitOr(_) 101 | | BinOp::Shl(_) 102 | | BinOp::Shr(_) 103 | | BinOp::Eq(_) 104 | | BinOp::Lt(_) 105 | | BinOp::Le(_) 106 | | BinOp::Ne(_) 107 | | BinOp::Ge(_) 108 | | BinOp::Gt(_) => false, 109 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 110 | _ => unimplemented!("unknown BinOp"), 111 | }, 112 | Expr::Group(group) => add_semi(&group.expr), 113 | 114 | Expr::Array(_) 115 | | Expr::Async(_) 116 | | Expr::Await(_) 117 | | Expr::Block(_) 118 | | Expr::Call(_) 119 | | Expr::Cast(_) 120 | | Expr::Closure(_) 121 | | Expr::Const(_) 122 | | Expr::Field(_) 123 | | Expr::ForLoop(_) 124 | | Expr::If(_) 125 | | Expr::Index(_) 126 | | Expr::Infer(_) 127 | | Expr::Let(_) 128 | | Expr::Lit(_) 129 | | Expr::Loop(_) 130 | | Expr::Macro(_) 131 | | Expr::Match(_) 132 | | Expr::MethodCall(_) 133 | | Expr::Paren(_) 134 | | Expr::Path(_) 135 | | Expr::Range(_) 136 | | Expr::Reference(_) 137 | | Expr::Repeat(_) 138 | | Expr::Struct(_) 139 | | Expr::Try(_) 140 | | Expr::TryBlock(_) 141 | | Expr::Tuple(_) 142 | | Expr::Unary(_) 143 | | Expr::Unsafe(_) 144 | | Expr::Verbatim(_) 145 | | Expr::While(_) => false, 146 | 147 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 148 | _ => false, 149 | } 150 | } 151 | 152 | pub fn break_after(expr: &Expr) -> bool { 153 | if let Expr::Group(group) = expr { 154 | if let Expr::Verbatim(verbatim) = group.expr.as_ref() { 155 | return !verbatim.is_empty(); 156 | } 157 | } 158 | true 159 | } 160 | 161 | fn remove_semi(expr: &Expr) -> bool { 162 | match expr { 163 | Expr::ForLoop(_) | Expr::While(_) => true, 164 | Expr::Group(group) => remove_semi(&group.expr), 165 | Expr::If(expr) => match &expr.else_branch { 166 | Some((_else_token, else_branch)) => remove_semi(else_branch), 167 | None => true, 168 | }, 169 | 170 | Expr::Array(_) 171 | | Expr::Assign(_) 172 | | Expr::Async(_) 173 | | Expr::Await(_) 174 | | Expr::Binary(_) 175 | | Expr::Block(_) 176 | | Expr::Break(_) 177 | | Expr::Call(_) 178 | | Expr::Cast(_) 179 | | Expr::Closure(_) 180 | | Expr::Continue(_) 181 | | Expr::Const(_) 182 | | Expr::Field(_) 183 | | Expr::Index(_) 184 | | Expr::Infer(_) 185 | | Expr::Let(_) 186 | | Expr::Lit(_) 187 | | Expr::Loop(_) 188 | | Expr::Macro(_) 189 | | Expr::Match(_) 190 | | Expr::MethodCall(_) 191 | | Expr::Paren(_) 192 | | Expr::Path(_) 193 | | Expr::Range(_) 194 | | Expr::Reference(_) 195 | | Expr::Repeat(_) 196 | | Expr::Return(_) 197 | | Expr::Struct(_) 198 | | Expr::Try(_) 199 | | Expr::TryBlock(_) 200 | | Expr::Tuple(_) 201 | | Expr::Unary(_) 202 | | Expr::Unsafe(_) 203 | | Expr::Verbatim(_) 204 | | Expr::Yield(_) => false, 205 | 206 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 207 | _ => false, 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/path.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::iter::IterDelimited; 3 | use crate::INDENT; 4 | use std::ptr; 5 | use syn::{ 6 | AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, Expr, GenericArgument, 7 | ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf, 8 | }; 9 | 10 | #[derive(Copy, Clone, PartialEq)] 11 | pub enum PathKind { 12 | // a::B 13 | Simple, 14 | // a::B 15 | Type, 16 | // a::B:: 17 | Expr, 18 | } 19 | 20 | impl Printer { 21 | pub fn path(&mut self, path: &Path, kind: PathKind) { 22 | assert!(!path.segments.is_empty()); 23 | for segment in path.segments.iter().delimited() { 24 | if !segment.is_first || path.leading_colon.is_some() { 25 | self.word("::"); 26 | } 27 | self.path_segment(&segment, kind); 28 | } 29 | } 30 | 31 | pub fn path_segment(&mut self, segment: &PathSegment, kind: PathKind) { 32 | self.ident(&segment.ident); 33 | self.path_arguments(&segment.arguments, kind); 34 | } 35 | 36 | fn path_arguments(&mut self, arguments: &PathArguments, kind: PathKind) { 37 | match arguments { 38 | PathArguments::None => {} 39 | PathArguments::AngleBracketed(arguments) => { 40 | self.angle_bracketed_generic_arguments(arguments, kind); 41 | } 42 | PathArguments::Parenthesized(arguments) => { 43 | self.parenthesized_generic_arguments(arguments); 44 | } 45 | } 46 | } 47 | 48 | fn generic_argument(&mut self, arg: &GenericArgument) { 49 | match arg { 50 | GenericArgument::Lifetime(lifetime) => self.lifetime(lifetime), 51 | GenericArgument::Type(ty) => self.ty(ty), 52 | GenericArgument::Const(expr) => { 53 | match expr { 54 | Expr::Lit(expr) => self.expr_lit(expr), 55 | Expr::Block(expr) => self.expr_block(expr), 56 | // ERROR CORRECTION: Add braces to make sure that the 57 | // generated code is valid. 58 | _ => { 59 | self.word("{"); 60 | self.expr(expr); 61 | self.word("}"); 62 | } 63 | } 64 | } 65 | GenericArgument::AssocType(assoc) => self.assoc_type(assoc), 66 | GenericArgument::AssocConst(assoc) => self.assoc_const(assoc), 67 | GenericArgument::Constraint(constraint) => self.constraint(constraint), 68 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 69 | _ => unimplemented!("unknown GenericArgument"), 70 | } 71 | } 72 | 73 | pub fn angle_bracketed_generic_arguments( 74 | &mut self, 75 | generic: &AngleBracketedGenericArguments, 76 | path_kind: PathKind, 77 | ) { 78 | if generic.args.is_empty() || path_kind == PathKind::Simple { 79 | return; 80 | } 81 | 82 | if path_kind == PathKind::Expr { 83 | self.word("::"); 84 | } 85 | self.word("<"); 86 | self.cbox(INDENT); 87 | self.zerobreak(); 88 | 89 | // Print lifetimes before types/consts/bindings, regardless of their 90 | // order in self.args. 91 | #[derive(Ord, PartialOrd, Eq, PartialEq)] 92 | enum Group { 93 | First, 94 | Second, 95 | } 96 | fn group(arg: &GenericArgument) -> Group { 97 | match arg { 98 | GenericArgument::Lifetime(_) => Group::First, 99 | GenericArgument::Type(_) 100 | | GenericArgument::Const(_) 101 | | GenericArgument::AssocType(_) 102 | | GenericArgument::AssocConst(_) 103 | | GenericArgument::Constraint(_) => Group::Second, 104 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 105 | _ => Group::Second, 106 | } 107 | } 108 | let last = generic.args.iter().max_by_key(|param| group(param)); 109 | for current_group in [Group::First, Group::Second] { 110 | for arg in &generic.args { 111 | if group(arg) == current_group { 112 | self.generic_argument(arg); 113 | self.trailing_comma(ptr::eq(arg, last.unwrap())); 114 | } 115 | } 116 | } 117 | 118 | self.offset(-INDENT); 119 | self.end(); 120 | self.word(">"); 121 | } 122 | 123 | fn assoc_type(&mut self, assoc: &AssocType) { 124 | self.ident(&assoc.ident); 125 | if let Some(generics) = &assoc.generics { 126 | self.angle_bracketed_generic_arguments(generics, PathKind::Type); 127 | } 128 | self.word(" = "); 129 | self.ty(&assoc.ty); 130 | } 131 | 132 | fn assoc_const(&mut self, assoc: &AssocConst) { 133 | self.ident(&assoc.ident); 134 | if let Some(generics) = &assoc.generics { 135 | self.angle_bracketed_generic_arguments(generics, PathKind::Type); 136 | } 137 | self.word(" = "); 138 | self.expr(&assoc.value); 139 | } 140 | 141 | fn constraint(&mut self, constraint: &Constraint) { 142 | self.ident(&constraint.ident); 143 | if let Some(generics) = &constraint.generics { 144 | self.angle_bracketed_generic_arguments(generics, PathKind::Type); 145 | } 146 | self.ibox(INDENT); 147 | for bound in constraint.bounds.iter().delimited() { 148 | if bound.is_first { 149 | self.word(": "); 150 | } else { 151 | self.space(); 152 | self.word("+ "); 153 | } 154 | self.type_param_bound(&bound); 155 | } 156 | self.end(); 157 | } 158 | 159 | fn parenthesized_generic_arguments(&mut self, arguments: &ParenthesizedGenericArguments) { 160 | self.cbox(INDENT); 161 | self.word("("); 162 | self.zerobreak(); 163 | for ty in arguments.inputs.iter().delimited() { 164 | self.ty(&ty); 165 | self.trailing_comma(ty.is_last); 166 | } 167 | self.offset(-INDENT); 168 | self.word(")"); 169 | self.return_type(&arguments.output); 170 | self.end(); 171 | } 172 | 173 | pub fn qpath(&mut self, qself: &Option, path: &Path, kind: PathKind) { 174 | let qself = match qself { 175 | Some(qself) => qself, 176 | None => { 177 | self.path(path, kind); 178 | return; 179 | } 180 | }; 181 | 182 | assert!(qself.position < path.segments.len()); 183 | 184 | self.word("<"); 185 | self.ty(&qself.ty); 186 | 187 | let mut segments = path.segments.iter(); 188 | if qself.position > 0 { 189 | self.word(" as "); 190 | for segment in segments.by_ref().take(qself.position).delimited() { 191 | if !segment.is_first || path.leading_colon.is_some() { 192 | self.word("::"); 193 | } 194 | self.path_segment(&segment, PathKind::Type); 195 | if segment.is_last { 196 | self.word(">"); 197 | } 198 | } 199 | } else { 200 | self.word(">"); 201 | } 202 | for segment in segments { 203 | self.word("::"); 204 | self.path_segment(segment, kind); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/pat.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::iter::IterDelimited; 3 | use crate::path::PathKind; 4 | use crate::INDENT; 5 | use proc_macro2::TokenStream; 6 | use syn::{ 7 | FieldPat, Pat, PatIdent, PatOr, PatParen, PatReference, PatRest, PatSlice, PatStruct, PatTuple, 8 | PatTupleStruct, PatType, PatWild, 9 | }; 10 | 11 | impl Printer { 12 | pub fn pat(&mut self, pat: &Pat) { 13 | match pat { 14 | Pat::Const(pat) => self.expr_const(pat), 15 | Pat::Ident(pat) => self.pat_ident(pat), 16 | Pat::Lit(pat) => self.expr_lit(pat), 17 | Pat::Macro(pat) => self.expr_macro(pat), 18 | Pat::Or(pat) => self.pat_or(pat), 19 | Pat::Paren(pat) => self.pat_paren(pat), 20 | Pat::Path(pat) => self.expr_path(pat), 21 | Pat::Range(pat) => self.expr_range(pat), 22 | Pat::Reference(pat) => self.pat_reference(pat), 23 | Pat::Rest(pat) => self.pat_rest(pat), 24 | Pat::Slice(pat) => self.pat_slice(pat), 25 | Pat::Struct(pat) => self.pat_struct(pat), 26 | Pat::Tuple(pat) => self.pat_tuple(pat), 27 | Pat::TupleStruct(pat) => self.pat_tuple_struct(pat), 28 | Pat::Type(pat) => self.pat_type(pat), 29 | Pat::Verbatim(pat) => self.pat_verbatim(pat), 30 | Pat::Wild(pat) => self.pat_wild(pat), 31 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 32 | _ => unimplemented!("unknown Pat"), 33 | } 34 | } 35 | 36 | fn pat_ident(&mut self, pat: &PatIdent) { 37 | self.outer_attrs(&pat.attrs); 38 | if pat.by_ref.is_some() { 39 | self.word("ref "); 40 | } 41 | if pat.mutability.is_some() { 42 | self.word("mut "); 43 | } 44 | self.ident(&pat.ident); 45 | if let Some((_at_token, subpat)) = &pat.subpat { 46 | self.word(" @ "); 47 | self.pat(subpat); 48 | } 49 | } 50 | 51 | fn pat_or(&mut self, pat: &PatOr) { 52 | self.outer_attrs(&pat.attrs); 53 | let mut consistent_break = false; 54 | for case in &pat.cases { 55 | match case { 56 | Pat::Lit(_) | Pat::Wild(_) => {} 57 | _ => { 58 | consistent_break = true; 59 | break; 60 | } 61 | } 62 | } 63 | if consistent_break { 64 | self.cbox(0); 65 | } else { 66 | self.ibox(0); 67 | } 68 | for case in pat.cases.iter().delimited() { 69 | if !case.is_first { 70 | self.space(); 71 | self.word("| "); 72 | } 73 | self.pat(&case); 74 | } 75 | self.end(); 76 | } 77 | 78 | fn pat_paren(&mut self, pat: &PatParen) { 79 | self.outer_attrs(&pat.attrs); 80 | self.word("("); 81 | self.pat(&pat.pat); 82 | self.word(")"); 83 | } 84 | 85 | fn pat_reference(&mut self, pat: &PatReference) { 86 | self.outer_attrs(&pat.attrs); 87 | self.word("&"); 88 | if pat.mutability.is_some() { 89 | self.word("mut "); 90 | } 91 | self.pat(&pat.pat); 92 | } 93 | 94 | fn pat_rest(&mut self, pat: &PatRest) { 95 | self.outer_attrs(&pat.attrs); 96 | self.word(".."); 97 | } 98 | 99 | fn pat_slice(&mut self, pat: &PatSlice) { 100 | self.outer_attrs(&pat.attrs); 101 | self.word("["); 102 | for elem in pat.elems.iter().delimited() { 103 | self.pat(&elem); 104 | self.trailing_comma(elem.is_last); 105 | } 106 | self.word("]"); 107 | } 108 | 109 | fn pat_struct(&mut self, pat: &PatStruct) { 110 | self.outer_attrs(&pat.attrs); 111 | self.cbox(INDENT); 112 | self.path(&pat.path, PathKind::Expr); 113 | self.word(" {"); 114 | self.space_if_nonempty(); 115 | for field in pat.fields.iter().delimited() { 116 | self.field_pat(&field); 117 | self.trailing_comma_or_space(field.is_last && pat.rest.is_none()); 118 | } 119 | if let Some(rest) = &pat.rest { 120 | self.pat_rest(rest); 121 | self.space(); 122 | } 123 | self.offset(-INDENT); 124 | self.end(); 125 | self.word("}"); 126 | } 127 | 128 | fn pat_tuple(&mut self, pat: &PatTuple) { 129 | self.outer_attrs(&pat.attrs); 130 | self.word("("); 131 | self.cbox(INDENT); 132 | self.zerobreak(); 133 | for elem in pat.elems.iter().delimited() { 134 | self.pat(&elem); 135 | if pat.elems.len() == 1 { 136 | if pat.elems.trailing_punct() { 137 | self.word(","); 138 | } 139 | self.zerobreak(); 140 | } else { 141 | self.trailing_comma(elem.is_last); 142 | } 143 | } 144 | self.offset(-INDENT); 145 | self.end(); 146 | self.word(")"); 147 | } 148 | 149 | fn pat_tuple_struct(&mut self, pat: &PatTupleStruct) { 150 | self.outer_attrs(&pat.attrs); 151 | self.path(&pat.path, PathKind::Expr); 152 | self.word("("); 153 | self.cbox(INDENT); 154 | self.zerobreak(); 155 | for elem in pat.elems.iter().delimited() { 156 | self.pat(&elem); 157 | self.trailing_comma(elem.is_last); 158 | } 159 | self.offset(-INDENT); 160 | self.end(); 161 | self.word(")"); 162 | } 163 | 164 | pub fn pat_type(&mut self, pat: &PatType) { 165 | self.outer_attrs(&pat.attrs); 166 | self.pat(&pat.pat); 167 | self.word(": "); 168 | self.ty(&pat.ty); 169 | } 170 | 171 | #[cfg(not(feature = "verbatim"))] 172 | fn pat_verbatim(&mut self, pat: &TokenStream) { 173 | unimplemented!("Pat::Verbatim `{}`", pat); 174 | } 175 | 176 | #[cfg(feature = "verbatim")] 177 | fn pat_verbatim(&mut self, tokens: &TokenStream) { 178 | use syn::parse::{Parse, ParseStream, Result}; 179 | use syn::{braced, Attribute, Block, Token}; 180 | 181 | enum PatVerbatim { 182 | Ellipsis, 183 | Box(Pat), 184 | Const(PatConst), 185 | } 186 | 187 | struct PatConst { 188 | attrs: Vec, 189 | block: Block, 190 | } 191 | 192 | impl Parse for PatVerbatim { 193 | fn parse(input: ParseStream) -> Result { 194 | let lookahead = input.lookahead1(); 195 | if lookahead.peek(Token![box]) { 196 | input.parse::()?; 197 | let inner = Pat::parse_single(input)?; 198 | Ok(PatVerbatim::Box(inner)) 199 | } else if lookahead.peek(Token![const]) { 200 | input.parse::()?; 201 | let content; 202 | let brace_token = braced!(content in input); 203 | let attrs = content.call(Attribute::parse_inner)?; 204 | let stmts = content.call(Block::parse_within)?; 205 | Ok(PatVerbatim::Const(PatConst { 206 | attrs, 207 | block: Block { brace_token, stmts }, 208 | })) 209 | } else if lookahead.peek(Token![...]) { 210 | input.parse::()?; 211 | Ok(PatVerbatim::Ellipsis) 212 | } else { 213 | Err(lookahead.error()) 214 | } 215 | } 216 | } 217 | 218 | let pat: PatVerbatim = match syn::parse2(tokens.clone()) { 219 | Ok(pat) => pat, 220 | Err(_) => unimplemented!("Pat::Verbatim `{}`", tokens), 221 | }; 222 | 223 | match pat { 224 | PatVerbatim::Ellipsis => { 225 | self.word("..."); 226 | } 227 | PatVerbatim::Box(pat) => { 228 | self.word("box "); 229 | self.pat(&pat); 230 | } 231 | PatVerbatim::Const(pat) => { 232 | self.word("const "); 233 | self.cbox(INDENT); 234 | self.small_block(&pat.block, &pat.attrs); 235 | self.end(); 236 | } 237 | } 238 | } 239 | 240 | fn pat_wild(&mut self, pat: &PatWild) { 241 | self.outer_attrs(&pat.attrs); 242 | self.word("_"); 243 | } 244 | 245 | fn field_pat(&mut self, field_pat: &FieldPat) { 246 | self.outer_attrs(&field_pat.attrs); 247 | if field_pat.colon_token.is_some() { 248 | self.member(&field_pat.member); 249 | self.word(": "); 250 | } 251 | self.pat(&field_pat.pat); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/ty.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::iter::IterDelimited; 3 | use crate::path::PathKind; 4 | use crate::INDENT; 5 | use proc_macro2::TokenStream; 6 | use syn::{ 7 | Abi, BareFnArg, BareVariadic, ReturnType, Type, TypeArray, TypeBareFn, TypeGroup, 8 | TypeImplTrait, TypeInfer, TypeMacro, TypeNever, TypeParen, TypePath, TypePtr, TypeReference, 9 | TypeSlice, TypeTraitObject, TypeTuple, 10 | }; 11 | 12 | impl Printer { 13 | pub fn ty(&mut self, ty: &Type) { 14 | match ty { 15 | Type::Array(ty) => self.type_array(ty), 16 | Type::BareFn(ty) => self.type_bare_fn(ty), 17 | Type::Group(ty) => self.type_group(ty), 18 | Type::ImplTrait(ty) => self.type_impl_trait(ty), 19 | Type::Infer(ty) => self.type_infer(ty), 20 | Type::Macro(ty) => self.type_macro(ty), 21 | Type::Never(ty) => self.type_never(ty), 22 | Type::Paren(ty) => self.type_paren(ty), 23 | Type::Path(ty) => self.type_path(ty), 24 | Type::Ptr(ty) => self.type_ptr(ty), 25 | Type::Reference(ty) => self.type_reference(ty), 26 | Type::Slice(ty) => self.type_slice(ty), 27 | Type::TraitObject(ty) => self.type_trait_object(ty), 28 | Type::Tuple(ty) => self.type_tuple(ty), 29 | Type::Verbatim(ty) => self.type_verbatim(ty), 30 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 31 | _ => unimplemented!("unknown Type"), 32 | } 33 | } 34 | 35 | fn type_array(&mut self, ty: &TypeArray) { 36 | self.word("["); 37 | self.ty(&ty.elem); 38 | self.word("; "); 39 | self.expr(&ty.len); 40 | self.word("]"); 41 | } 42 | 43 | fn type_bare_fn(&mut self, ty: &TypeBareFn) { 44 | if let Some(bound_lifetimes) = &ty.lifetimes { 45 | self.bound_lifetimes(bound_lifetimes); 46 | } 47 | if ty.unsafety.is_some() { 48 | self.word("unsafe "); 49 | } 50 | if let Some(abi) = &ty.abi { 51 | self.abi(abi); 52 | } 53 | self.word("fn("); 54 | self.cbox(INDENT); 55 | self.zerobreak(); 56 | for bare_fn_arg in ty.inputs.iter().delimited() { 57 | self.bare_fn_arg(&bare_fn_arg); 58 | self.trailing_comma(bare_fn_arg.is_last && ty.variadic.is_none()); 59 | } 60 | if let Some(variadic) = &ty.variadic { 61 | self.bare_variadic(variadic); 62 | self.zerobreak(); 63 | } 64 | self.offset(-INDENT); 65 | self.end(); 66 | self.word(")"); 67 | self.return_type(&ty.output); 68 | } 69 | 70 | fn type_group(&mut self, ty: &TypeGroup) { 71 | self.ty(&ty.elem); 72 | } 73 | 74 | fn type_impl_trait(&mut self, ty: &TypeImplTrait) { 75 | self.word("impl "); 76 | for type_param_bound in ty.bounds.iter().delimited() { 77 | if !type_param_bound.is_first { 78 | self.word(" + "); 79 | } 80 | self.type_param_bound(&type_param_bound); 81 | } 82 | } 83 | 84 | fn type_infer(&mut self, ty: &TypeInfer) { 85 | let _ = ty; 86 | self.word("_"); 87 | } 88 | 89 | fn type_macro(&mut self, ty: &TypeMacro) { 90 | let semicolon = false; 91 | self.mac(&ty.mac, None, semicolon); 92 | } 93 | 94 | fn type_never(&mut self, ty: &TypeNever) { 95 | let _ = ty; 96 | self.word("!"); 97 | } 98 | 99 | fn type_paren(&mut self, ty: &TypeParen) { 100 | self.word("("); 101 | self.ty(&ty.elem); 102 | self.word(")"); 103 | } 104 | 105 | fn type_path(&mut self, ty: &TypePath) { 106 | self.qpath(&ty.qself, &ty.path, PathKind::Type); 107 | } 108 | 109 | fn type_ptr(&mut self, ty: &TypePtr) { 110 | self.word("*"); 111 | if ty.mutability.is_some() { 112 | self.word("mut "); 113 | } else { 114 | self.word("const "); 115 | } 116 | self.ty(&ty.elem); 117 | } 118 | 119 | fn type_reference(&mut self, ty: &TypeReference) { 120 | self.word("&"); 121 | if let Some(lifetime) = &ty.lifetime { 122 | self.lifetime(lifetime); 123 | self.nbsp(); 124 | } 125 | if ty.mutability.is_some() { 126 | self.word("mut "); 127 | } 128 | self.ty(&ty.elem); 129 | } 130 | 131 | fn type_slice(&mut self, ty: &TypeSlice) { 132 | self.word("["); 133 | self.ty(&ty.elem); 134 | self.word("]"); 135 | } 136 | 137 | fn type_trait_object(&mut self, ty: &TypeTraitObject) { 138 | self.word("dyn "); 139 | for type_param_bound in ty.bounds.iter().delimited() { 140 | if !type_param_bound.is_first { 141 | self.word(" + "); 142 | } 143 | self.type_param_bound(&type_param_bound); 144 | } 145 | } 146 | 147 | fn type_tuple(&mut self, ty: &TypeTuple) { 148 | self.word("("); 149 | self.cbox(INDENT); 150 | self.zerobreak(); 151 | for elem in ty.elems.iter().delimited() { 152 | self.ty(&elem); 153 | if ty.elems.len() == 1 { 154 | self.word(","); 155 | self.zerobreak(); 156 | } else { 157 | self.trailing_comma(elem.is_last); 158 | } 159 | } 160 | self.offset(-INDENT); 161 | self.end(); 162 | self.word(")"); 163 | } 164 | 165 | #[cfg(not(feature = "verbatim"))] 166 | fn type_verbatim(&mut self, ty: &TokenStream) { 167 | unimplemented!("Type::Verbatim `{}`", ty); 168 | } 169 | 170 | #[cfg(feature = "verbatim")] 171 | fn type_verbatim(&mut self, tokens: &TokenStream) { 172 | use syn::parse::{Parse, ParseStream, Result}; 173 | use syn::punctuated::Punctuated; 174 | use syn::{Token, TypeParamBound}; 175 | 176 | enum TypeVerbatim { 177 | Ellipsis, 178 | DynStar(DynStar), 179 | MutSelf(MutSelf), 180 | NotType(NotType), 181 | } 182 | 183 | struct DynStar { 184 | bounds: Punctuated, 185 | } 186 | 187 | struct MutSelf { 188 | ty: Option, 189 | } 190 | 191 | struct NotType { 192 | inner: Type, 193 | } 194 | 195 | impl Parse for TypeVerbatim { 196 | fn parse(input: ParseStream) -> Result { 197 | let lookahead = input.lookahead1(); 198 | if lookahead.peek(Token![dyn]) { 199 | input.parse::()?; 200 | input.parse::()?; 201 | let bounds = input.parse_terminated(TypeParamBound::parse, Token![+])?; 202 | Ok(TypeVerbatim::DynStar(DynStar { bounds })) 203 | } else if lookahead.peek(Token![mut]) { 204 | input.parse::()?; 205 | input.parse::()?; 206 | let ty = if input.is_empty() { 207 | None 208 | } else { 209 | input.parse::()?; 210 | let ty: Type = input.parse()?; 211 | Some(ty) 212 | }; 213 | Ok(TypeVerbatim::MutSelf(MutSelf { ty })) 214 | } else if lookahead.peek(Token![!]) { 215 | input.parse::()?; 216 | let inner: Type = input.parse()?; 217 | Ok(TypeVerbatim::NotType(NotType { inner })) 218 | } else if lookahead.peek(Token![...]) { 219 | input.parse::()?; 220 | Ok(TypeVerbatim::Ellipsis) 221 | } else { 222 | Err(lookahead.error()) 223 | } 224 | } 225 | } 226 | 227 | let ty: TypeVerbatim = match syn::parse2(tokens.clone()) { 228 | Ok(ty) => ty, 229 | Err(_) => unimplemented!("Type::Verbatim `{}`", tokens), 230 | }; 231 | 232 | match ty { 233 | TypeVerbatim::Ellipsis => { 234 | self.word("..."); 235 | } 236 | TypeVerbatim::DynStar(ty) => { 237 | self.word("dyn* "); 238 | for type_param_bound in ty.bounds.iter().delimited() { 239 | if !type_param_bound.is_first { 240 | self.word(" + "); 241 | } 242 | self.type_param_bound(&type_param_bound); 243 | } 244 | } 245 | TypeVerbatim::MutSelf(bare_fn_arg) => { 246 | self.word("mut self"); 247 | if let Some(ty) = &bare_fn_arg.ty { 248 | self.word(": "); 249 | self.ty(ty); 250 | } 251 | } 252 | TypeVerbatim::NotType(ty) => { 253 | self.word("!"); 254 | self.ty(&ty.inner); 255 | } 256 | } 257 | } 258 | 259 | pub fn return_type(&mut self, ty: &ReturnType) { 260 | match ty { 261 | ReturnType::Default => {} 262 | ReturnType::Type(_arrow, ty) => { 263 | self.word(" -> "); 264 | self.ty(ty); 265 | } 266 | } 267 | } 268 | 269 | fn bare_fn_arg(&mut self, bare_fn_arg: &BareFnArg) { 270 | self.outer_attrs(&bare_fn_arg.attrs); 271 | if let Some((name, _colon)) = &bare_fn_arg.name { 272 | self.ident(name); 273 | self.word(": "); 274 | } 275 | self.ty(&bare_fn_arg.ty); 276 | } 277 | 278 | fn bare_variadic(&mut self, variadic: &BareVariadic) { 279 | self.outer_attrs(&variadic.attrs); 280 | if let Some((name, _colon)) = &variadic.name { 281 | self.ident(name); 282 | self.word(": "); 283 | } 284 | self.word("..."); 285 | } 286 | 287 | pub fn abi(&mut self, abi: &Abi) { 288 | self.word("extern "); 289 | if let Some(name) = &abi.name { 290 | self.lit_str(name); 291 | self.nbsp(); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /src/attr.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::path::PathKind; 3 | use crate::INDENT; 4 | use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; 5 | use syn::{AttrStyle, Attribute, Expr, Lit, MacroDelimiter, Meta, MetaList, MetaNameValue}; 6 | 7 | impl Printer { 8 | pub fn outer_attrs(&mut self, attrs: &[Attribute]) { 9 | for attr in attrs { 10 | if let AttrStyle::Outer = attr.style { 11 | self.attr(attr); 12 | } 13 | } 14 | } 15 | 16 | pub fn inner_attrs(&mut self, attrs: &[Attribute]) { 17 | for attr in attrs { 18 | if let AttrStyle::Inner(_) = attr.style { 19 | self.attr(attr); 20 | } 21 | } 22 | } 23 | 24 | fn attr(&mut self, attr: &Attribute) { 25 | if let Some(mut doc) = value_of_attribute("doc", attr) { 26 | if !doc.contains('\n') 27 | && match attr.style { 28 | AttrStyle::Outer => !doc.starts_with('/'), 29 | AttrStyle::Inner(_) => true, 30 | } 31 | { 32 | trim_trailing_spaces(&mut doc); 33 | self.word(match attr.style { 34 | AttrStyle::Outer => "///", 35 | AttrStyle::Inner(_) => "//!", 36 | }); 37 | self.word(doc); 38 | self.hardbreak(); 39 | return; 40 | } else if can_be_block_comment(&doc) 41 | && match attr.style { 42 | AttrStyle::Outer => !doc.starts_with(&['*', '/'][..]), 43 | AttrStyle::Inner(_) => true, 44 | } 45 | { 46 | trim_interior_trailing_spaces(&mut doc); 47 | self.word(match attr.style { 48 | AttrStyle::Outer => "/**", 49 | AttrStyle::Inner(_) => "/*!", 50 | }); 51 | self.word(doc); 52 | self.word("*/"); 53 | self.hardbreak(); 54 | return; 55 | } 56 | } else if let Some(mut comment) = value_of_attribute("comment", attr) { 57 | if !comment.contains('\n') { 58 | trim_trailing_spaces(&mut comment); 59 | self.word("//"); 60 | self.word(comment); 61 | self.hardbreak(); 62 | return; 63 | } else if can_be_block_comment(&comment) && !comment.starts_with(&['*', '!'][..]) { 64 | trim_interior_trailing_spaces(&mut comment); 65 | self.word("/*"); 66 | self.word(comment); 67 | self.word("*/"); 68 | self.hardbreak(); 69 | return; 70 | } 71 | } 72 | 73 | self.word(match attr.style { 74 | AttrStyle::Outer => "#", 75 | AttrStyle::Inner(_) => "#!", 76 | }); 77 | self.word("["); 78 | self.meta(&attr.meta); 79 | self.word("]"); 80 | self.space(); 81 | } 82 | 83 | fn meta(&mut self, meta: &Meta) { 84 | match meta { 85 | Meta::Path(path) => self.path(path, PathKind::Simple), 86 | Meta::List(meta) => self.meta_list(meta), 87 | Meta::NameValue(meta) => self.meta_name_value(meta), 88 | } 89 | } 90 | 91 | fn meta_list(&mut self, meta: &MetaList) { 92 | self.path(&meta.path, PathKind::Simple); 93 | let delimiter = match meta.delimiter { 94 | MacroDelimiter::Paren(_) => Delimiter::Parenthesis, 95 | MacroDelimiter::Brace(_) => Delimiter::Brace, 96 | MacroDelimiter::Bracket(_) => Delimiter::Bracket, 97 | }; 98 | let group = Group::new(delimiter, meta.tokens.clone()); 99 | self.attr_tokens(TokenStream::from(TokenTree::Group(group))); 100 | } 101 | 102 | fn meta_name_value(&mut self, meta: &MetaNameValue) { 103 | self.path(&meta.path, PathKind::Simple); 104 | self.word(" = "); 105 | self.expr(&meta.value); 106 | } 107 | 108 | fn attr_tokens(&mut self, tokens: TokenStream) { 109 | let mut stack = Vec::new(); 110 | stack.push((tokens.into_iter().peekable(), Delimiter::None)); 111 | let mut space = Self::nbsp as fn(&mut Self); 112 | 113 | #[derive(PartialEq)] 114 | enum State { 115 | Word, 116 | Punct, 117 | TrailingComma, 118 | } 119 | 120 | use State::*; 121 | let mut state = Word; 122 | 123 | while let Some((tokens, delimiter)) = stack.last_mut() { 124 | match tokens.next() { 125 | Some(TokenTree::Ident(ident)) => { 126 | if let Word = state { 127 | space(self); 128 | } 129 | self.ident(&ident); 130 | state = Word; 131 | } 132 | Some(TokenTree::Punct(punct)) => { 133 | let ch = punct.as_char(); 134 | if let (Word, '=') = (state, ch) { 135 | self.nbsp(); 136 | } 137 | if ch == ',' && tokens.peek().is_none() { 138 | self.trailing_comma(true); 139 | state = TrailingComma; 140 | } else { 141 | self.token_punct(ch); 142 | if ch == '=' { 143 | self.nbsp(); 144 | } else if ch == ',' { 145 | space(self); 146 | } 147 | state = Punct; 148 | } 149 | } 150 | Some(TokenTree::Literal(literal)) => { 151 | if let Word = state { 152 | space(self); 153 | } 154 | self.token_literal(&literal); 155 | state = Word; 156 | } 157 | Some(TokenTree::Group(group)) => { 158 | let delimiter = group.delimiter(); 159 | let stream = group.stream(); 160 | match delimiter { 161 | Delimiter::Parenthesis => { 162 | self.word("("); 163 | self.cbox(INDENT); 164 | self.zerobreak(); 165 | state = Punct; 166 | } 167 | Delimiter::Brace => { 168 | self.word("{"); 169 | state = Punct; 170 | } 171 | Delimiter::Bracket => { 172 | self.word("["); 173 | state = Punct; 174 | } 175 | Delimiter::None => {} 176 | } 177 | stack.push((stream.into_iter().peekable(), delimiter)); 178 | space = Self::space; 179 | } 180 | None => { 181 | match delimiter { 182 | Delimiter::Parenthesis => { 183 | if state != TrailingComma { 184 | self.zerobreak(); 185 | } 186 | self.offset(-INDENT); 187 | self.end(); 188 | self.word(")"); 189 | state = Punct; 190 | } 191 | Delimiter::Brace => { 192 | self.word("}"); 193 | state = Punct; 194 | } 195 | Delimiter::Bracket => { 196 | self.word("]"); 197 | state = Punct; 198 | } 199 | Delimiter::None => {} 200 | } 201 | stack.pop(); 202 | if stack.is_empty() { 203 | space = Self::nbsp; 204 | } 205 | } 206 | } 207 | } 208 | } 209 | } 210 | 211 | fn value_of_attribute(requested: &str, attr: &Attribute) -> Option { 212 | let value = match &attr.meta { 213 | Meta::NameValue(meta) if meta.path.is_ident(requested) => &meta.value, 214 | _ => return None, 215 | }; 216 | let lit = match value { 217 | Expr::Lit(expr) if expr.attrs.is_empty() => &expr.lit, 218 | _ => return None, 219 | }; 220 | match lit { 221 | Lit::Str(string) => Some(string.value()), 222 | _ => None, 223 | } 224 | } 225 | 226 | pub fn has_outer(attrs: &[Attribute]) -> bool { 227 | for attr in attrs { 228 | if let AttrStyle::Outer = attr.style { 229 | return true; 230 | } 231 | } 232 | false 233 | } 234 | 235 | pub fn has_inner(attrs: &[Attribute]) -> bool { 236 | for attr in attrs { 237 | if let AttrStyle::Inner(_) = attr.style { 238 | return true; 239 | } 240 | } 241 | false 242 | } 243 | 244 | fn trim_trailing_spaces(doc: &mut String) { 245 | doc.truncate(doc.trim_end_matches(' ').len()); 246 | } 247 | 248 | fn trim_interior_trailing_spaces(doc: &mut String) { 249 | if !doc.contains(" \n") { 250 | return; 251 | } 252 | let mut trimmed = String::with_capacity(doc.len()); 253 | let mut lines = doc.split('\n').peekable(); 254 | while let Some(line) = lines.next() { 255 | if lines.peek().is_some() { 256 | trimmed.push_str(line.trim_end_matches(' ')); 257 | trimmed.push('\n'); 258 | } else { 259 | trimmed.push_str(line); 260 | } 261 | } 262 | *doc = trimmed; 263 | } 264 | 265 | fn can_be_block_comment(value: &str) -> bool { 266 | let mut depth = 0usize; 267 | let bytes = value.as_bytes(); 268 | let mut i = 0usize; 269 | let upper = bytes.len() - 1; 270 | 271 | while i < upper { 272 | if bytes[i] == b'/' && bytes[i + 1] == b'*' { 273 | depth += 1; 274 | i += 2; 275 | } else if bytes[i] == b'*' && bytes[i + 1] == b'/' { 276 | if depth == 0 { 277 | return false; 278 | } 279 | depth -= 1; 280 | i += 2; 281 | } else { 282 | i += 1; 283 | } 284 | } 285 | 286 | depth == 0 287 | } 288 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/generics.rs: -------------------------------------------------------------------------------- 1 | use crate::algorithm::Printer; 2 | use crate::iter::IterDelimited; 3 | use crate::path::PathKind; 4 | use crate::INDENT; 5 | use proc_macro2::TokenStream; 6 | use std::ptr; 7 | use syn::{ 8 | BoundLifetimes, ConstParam, GenericParam, Generics, LifetimeParam, PredicateLifetime, 9 | PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause, 10 | WherePredicate, 11 | }; 12 | 13 | impl Printer { 14 | pub fn generics(&mut self, generics: &Generics) { 15 | if generics.params.is_empty() { 16 | return; 17 | } 18 | 19 | self.word("<"); 20 | self.cbox(0); 21 | self.zerobreak(); 22 | 23 | // Print lifetimes before types and consts, regardless of their 24 | // order in self.params. 25 | #[derive(Ord, PartialOrd, Eq, PartialEq)] 26 | enum Group { 27 | First, 28 | Second, 29 | } 30 | fn group(param: &GenericParam) -> Group { 31 | match param { 32 | GenericParam::Lifetime(_) => Group::First, 33 | GenericParam::Type(_) | GenericParam::Const(_) => Group::Second, 34 | } 35 | } 36 | let last = generics.params.iter().max_by_key(|param| group(param)); 37 | for current_group in [Group::First, Group::Second] { 38 | for param in &generics.params { 39 | if group(param) == current_group { 40 | self.generic_param(param); 41 | self.trailing_comma(ptr::eq(param, last.unwrap())); 42 | } 43 | } 44 | } 45 | 46 | self.offset(-INDENT); 47 | self.end(); 48 | self.word(">"); 49 | } 50 | 51 | fn generic_param(&mut self, generic_param: &GenericParam) { 52 | match generic_param { 53 | GenericParam::Type(type_param) => self.type_param(type_param), 54 | GenericParam::Lifetime(lifetime_param) => self.lifetime_param(lifetime_param), 55 | GenericParam::Const(const_param) => self.const_param(const_param), 56 | } 57 | } 58 | 59 | pub fn bound_lifetimes(&mut self, bound_lifetimes: &BoundLifetimes) { 60 | self.word("for<"); 61 | for param in bound_lifetimes.lifetimes.iter().delimited() { 62 | self.generic_param(¶m); 63 | if !param.is_last { 64 | self.word(", "); 65 | } 66 | } 67 | self.word("> "); 68 | } 69 | 70 | fn lifetime_param(&mut self, lifetime_param: &LifetimeParam) { 71 | self.outer_attrs(&lifetime_param.attrs); 72 | self.lifetime(&lifetime_param.lifetime); 73 | for lifetime in lifetime_param.bounds.iter().delimited() { 74 | if lifetime.is_first { 75 | self.word(": "); 76 | } else { 77 | self.word(" + "); 78 | } 79 | self.lifetime(&lifetime); 80 | } 81 | } 82 | 83 | fn type_param(&mut self, type_param: &TypeParam) { 84 | self.outer_attrs(&type_param.attrs); 85 | self.ident(&type_param.ident); 86 | self.ibox(INDENT); 87 | for type_param_bound in type_param.bounds.iter().delimited() { 88 | if type_param_bound.is_first { 89 | self.word(": "); 90 | } else { 91 | self.space(); 92 | self.word("+ "); 93 | } 94 | self.type_param_bound(&type_param_bound); 95 | } 96 | if let Some(default) = &type_param.default { 97 | self.space(); 98 | self.word("= "); 99 | self.ty(default); 100 | } 101 | self.end(); 102 | } 103 | 104 | pub fn type_param_bound(&mut self, type_param_bound: &TypeParamBound) { 105 | match type_param_bound { 106 | TypeParamBound::Trait(trait_bound) => { 107 | let tilde_const = false; 108 | self.trait_bound(trait_bound, tilde_const); 109 | } 110 | TypeParamBound::Lifetime(lifetime) => self.lifetime(lifetime), 111 | TypeParamBound::Verbatim(bound) => self.type_param_bound_verbatim(bound), 112 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 113 | _ => unimplemented!("unknown TypeParamBound"), 114 | } 115 | } 116 | 117 | fn trait_bound(&mut self, trait_bound: &TraitBound, tilde_const: bool) { 118 | if trait_bound.paren_token.is_some() { 119 | self.word("("); 120 | } 121 | if tilde_const { 122 | self.word("~const "); 123 | } 124 | self.trait_bound_modifier(&trait_bound.modifier); 125 | if let Some(bound_lifetimes) = &trait_bound.lifetimes { 126 | self.bound_lifetimes(bound_lifetimes); 127 | } 128 | for segment in trait_bound.path.segments.iter().delimited() { 129 | if !segment.is_first || trait_bound.path.leading_colon.is_some() { 130 | self.word("::"); 131 | } 132 | self.path_segment(&segment, PathKind::Type); 133 | } 134 | if trait_bound.paren_token.is_some() { 135 | self.word(")"); 136 | } 137 | } 138 | 139 | fn trait_bound_modifier(&mut self, trait_bound_modifier: &TraitBoundModifier) { 140 | match trait_bound_modifier { 141 | TraitBoundModifier::None => {} 142 | TraitBoundModifier::Maybe(_question_mark) => self.word("?"), 143 | } 144 | } 145 | 146 | #[cfg(not(feature = "verbatim"))] 147 | fn type_param_bound_verbatim(&mut self, bound: &TokenStream) { 148 | unimplemented!("TypeParamBound::Verbatim `{}`", bound); 149 | } 150 | 151 | #[cfg(feature = "verbatim")] 152 | fn type_param_bound_verbatim(&mut self, tokens: &TokenStream) { 153 | use syn::parse::{Parse, ParseStream, Result}; 154 | use syn::{parenthesized, token, Token}; 155 | 156 | enum TypeParamBoundVerbatim { 157 | Ellipsis, 158 | TildeConst(TraitBound), 159 | } 160 | 161 | impl Parse for TypeParamBoundVerbatim { 162 | fn parse(input: ParseStream) -> Result { 163 | let content; 164 | let (paren_token, content) = if input.peek(token::Paren) { 165 | (Some(parenthesized!(content in input)), &content) 166 | } else { 167 | (None, input) 168 | }; 169 | let lookahead = content.lookahead1(); 170 | if lookahead.peek(Token![~]) { 171 | content.parse::()?; 172 | content.parse::()?; 173 | let mut bound: TraitBound = content.parse()?; 174 | bound.paren_token = paren_token; 175 | Ok(TypeParamBoundVerbatim::TildeConst(bound)) 176 | } else if lookahead.peek(Token![...]) { 177 | content.parse::()?; 178 | Ok(TypeParamBoundVerbatim::Ellipsis) 179 | } else { 180 | Err(lookahead.error()) 181 | } 182 | } 183 | } 184 | 185 | let bound: TypeParamBoundVerbatim = match syn::parse2(tokens.clone()) { 186 | Ok(bound) => bound, 187 | Err(_) => unimplemented!("TypeParamBound::Verbatim `{}`", tokens), 188 | }; 189 | 190 | match bound { 191 | TypeParamBoundVerbatim::Ellipsis => { 192 | self.word("..."); 193 | } 194 | TypeParamBoundVerbatim::TildeConst(trait_bound) => { 195 | let tilde_const = true; 196 | self.trait_bound(&trait_bound, tilde_const); 197 | } 198 | } 199 | } 200 | 201 | fn const_param(&mut self, const_param: &ConstParam) { 202 | self.outer_attrs(&const_param.attrs); 203 | self.word("const "); 204 | self.ident(&const_param.ident); 205 | self.word(": "); 206 | self.ty(&const_param.ty); 207 | if let Some(default) = &const_param.default { 208 | self.word(" = "); 209 | self.expr(default); 210 | } 211 | } 212 | 213 | pub fn where_clause_for_body(&mut self, where_clause: &Option) { 214 | let hardbreaks = true; 215 | let semi = false; 216 | self.where_clause_impl(where_clause, hardbreaks, semi); 217 | } 218 | 219 | pub fn where_clause_semi(&mut self, where_clause: &Option) { 220 | let hardbreaks = true; 221 | let semi = true; 222 | self.where_clause_impl(where_clause, hardbreaks, semi); 223 | } 224 | 225 | pub fn where_clause_oneline(&mut self, where_clause: &Option) { 226 | let hardbreaks = false; 227 | let semi = false; 228 | self.where_clause_impl(where_clause, hardbreaks, semi); 229 | } 230 | 231 | pub fn where_clause_oneline_semi(&mut self, where_clause: &Option) { 232 | let hardbreaks = false; 233 | let semi = true; 234 | self.where_clause_impl(where_clause, hardbreaks, semi); 235 | } 236 | 237 | fn where_clause_impl( 238 | &mut self, 239 | where_clause: &Option, 240 | hardbreaks: bool, 241 | semi: bool, 242 | ) { 243 | let where_clause = match where_clause { 244 | Some(where_clause) if !where_clause.predicates.is_empty() => where_clause, 245 | _ => { 246 | if semi { 247 | self.word(";"); 248 | } else { 249 | self.nbsp(); 250 | } 251 | return; 252 | } 253 | }; 254 | if hardbreaks { 255 | self.hardbreak(); 256 | self.offset(-INDENT); 257 | self.word("where"); 258 | self.hardbreak(); 259 | for predicate in where_clause.predicates.iter().delimited() { 260 | self.where_predicate(&predicate); 261 | if predicate.is_last && semi { 262 | self.word(";"); 263 | } else { 264 | self.word(","); 265 | self.hardbreak(); 266 | } 267 | } 268 | if !semi { 269 | self.offset(-INDENT); 270 | } 271 | } else { 272 | self.space(); 273 | self.offset(-INDENT); 274 | self.word("where"); 275 | self.space(); 276 | for predicate in where_clause.predicates.iter().delimited() { 277 | self.where_predicate(&predicate); 278 | if predicate.is_last && semi { 279 | self.word(";"); 280 | } else { 281 | self.trailing_comma_or_space(predicate.is_last); 282 | } 283 | } 284 | if !semi { 285 | self.offset(-INDENT); 286 | } 287 | } 288 | } 289 | 290 | fn where_predicate(&mut self, predicate: &WherePredicate) { 291 | match predicate { 292 | WherePredicate::Type(predicate) => self.predicate_type(predicate), 293 | WherePredicate::Lifetime(predicate) => self.predicate_lifetime(predicate), 294 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 295 | _ => unimplemented!("unknown WherePredicate"), 296 | } 297 | } 298 | 299 | fn predicate_type(&mut self, predicate: &PredicateType) { 300 | if let Some(bound_lifetimes) = &predicate.lifetimes { 301 | self.bound_lifetimes(bound_lifetimes); 302 | } 303 | self.ty(&predicate.bounded_ty); 304 | self.word(":"); 305 | if predicate.bounds.len() == 1 { 306 | self.ibox(0); 307 | } else { 308 | self.ibox(INDENT); 309 | } 310 | for type_param_bound in predicate.bounds.iter().delimited() { 311 | if type_param_bound.is_first { 312 | self.nbsp(); 313 | } else { 314 | self.space(); 315 | self.word("+ "); 316 | } 317 | self.type_param_bound(&type_param_bound); 318 | } 319 | self.end(); 320 | } 321 | 322 | fn predicate_lifetime(&mut self, predicate: &PredicateLifetime) { 323 | self.lifetime(&predicate.lifetime); 324 | self.word(":"); 325 | self.ibox(INDENT); 326 | for lifetime in predicate.bounds.iter().delimited() { 327 | if lifetime.is_first { 328 | self.nbsp(); 329 | } else { 330 | self.space(); 331 | self.word("+ "); 332 | } 333 | self.lifetime(&lifetime); 334 | } 335 | self.end(); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/algorithm.rs: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/rust-lang/rust/blob/1.57.0/compiler/rustc_ast_pretty/src/pp.rs. 2 | // See "Algorithm notes" in the crate-level rustdoc. 3 | 4 | use crate::ring::RingBuffer; 5 | use crate::{MARGIN, MIN_SPACE}; 6 | use std::borrow::Cow; 7 | use std::cmp; 8 | use std::collections::VecDeque; 9 | use std::iter; 10 | 11 | #[derive(Clone, Copy, PartialEq)] 12 | pub enum Breaks { 13 | Consistent, 14 | Inconsistent, 15 | } 16 | 17 | #[derive(Clone, Copy, Default)] 18 | pub struct BreakToken { 19 | pub offset: isize, 20 | pub blank_space: usize, 21 | pub pre_break: Option, 22 | pub post_break: Option, 23 | pub no_break: Option, 24 | pub if_nonempty: bool, 25 | pub never_break: bool, 26 | } 27 | 28 | #[derive(Clone, Copy)] 29 | pub struct BeginToken { 30 | pub offset: isize, 31 | pub breaks: Breaks, 32 | } 33 | 34 | #[derive(Clone)] 35 | pub enum Token { 36 | String(Cow<'static, str>), 37 | Break(BreakToken), 38 | Begin(BeginToken), 39 | End, 40 | } 41 | 42 | #[derive(Copy, Clone)] 43 | enum PrintFrame { 44 | Fits(Breaks), 45 | Broken(usize, Breaks), 46 | } 47 | 48 | pub const SIZE_INFINITY: isize = 0xffff; 49 | 50 | pub struct Printer { 51 | out: String, 52 | // Number of spaces left on line 53 | space: isize, 54 | // Ring-buffer of tokens and calculated sizes 55 | buf: RingBuffer, 56 | // Total size of tokens already printed 57 | left_total: isize, 58 | // Total size of tokens enqueued, including printed and not yet printed 59 | right_total: isize, 60 | // Holds the ring-buffer index of the Begin that started the current block, 61 | // possibly with the most recent Break after that Begin (if there is any) on 62 | // top of it. Values are pushed and popped on the back of the queue using it 63 | // like stack, and elsewhere old values are popped from the front of the 64 | // queue as they become irrelevant due to the primary ring-buffer advancing. 65 | scan_stack: VecDeque, 66 | // Stack of blocks-in-progress being flushed by print 67 | print_stack: Vec, 68 | // Level of indentation of current line 69 | indent: usize, 70 | // Buffered indentation to avoid writing trailing whitespace 71 | pending_indentation: usize, 72 | } 73 | 74 | #[derive(Clone)] 75 | struct BufEntry { 76 | token: Token, 77 | size: isize, 78 | } 79 | 80 | impl Printer { 81 | pub fn new() -> Self { 82 | Printer { 83 | out: String::new(), 84 | space: MARGIN, 85 | buf: RingBuffer::new(), 86 | left_total: 0, 87 | right_total: 0, 88 | scan_stack: VecDeque::new(), 89 | print_stack: Vec::new(), 90 | indent: 0, 91 | pending_indentation: 0, 92 | } 93 | } 94 | 95 | pub fn eof(mut self) -> String { 96 | if !self.scan_stack.is_empty() { 97 | self.check_stack(0); 98 | self.advance_left(); 99 | } 100 | self.out 101 | } 102 | 103 | pub fn scan_begin(&mut self, token: BeginToken) { 104 | if self.scan_stack.is_empty() { 105 | self.left_total = 1; 106 | self.right_total = 1; 107 | self.buf.clear(); 108 | } 109 | let right = self.buf.push(BufEntry { 110 | token: Token::Begin(token), 111 | size: -self.right_total, 112 | }); 113 | self.scan_stack.push_back(right); 114 | } 115 | 116 | pub fn scan_end(&mut self) { 117 | if self.scan_stack.is_empty() { 118 | self.print_end(); 119 | } else { 120 | if !self.buf.is_empty() { 121 | if let Token::Break(break_token) = self.buf.last().token { 122 | if self.buf.len() >= 2 { 123 | if let Token::Begin(_) = self.buf.second_last().token { 124 | self.buf.pop_last(); 125 | self.buf.pop_last(); 126 | self.scan_stack.pop_back(); 127 | self.scan_stack.pop_back(); 128 | self.right_total -= break_token.blank_space as isize; 129 | return; 130 | } 131 | } 132 | if break_token.if_nonempty { 133 | self.buf.pop_last(); 134 | self.scan_stack.pop_back(); 135 | self.right_total -= break_token.blank_space as isize; 136 | } 137 | } 138 | } 139 | let right = self.buf.push(BufEntry { 140 | token: Token::End, 141 | size: -1, 142 | }); 143 | self.scan_stack.push_back(right); 144 | } 145 | } 146 | 147 | pub fn scan_break(&mut self, token: BreakToken) { 148 | if self.scan_stack.is_empty() { 149 | self.left_total = 1; 150 | self.right_total = 1; 151 | self.buf.clear(); 152 | } else { 153 | self.check_stack(0); 154 | } 155 | let right = self.buf.push(BufEntry { 156 | token: Token::Break(token), 157 | size: -self.right_total, 158 | }); 159 | self.scan_stack.push_back(right); 160 | self.right_total += token.blank_space as isize; 161 | } 162 | 163 | pub fn scan_string(&mut self, string: Cow<'static, str>) { 164 | if self.scan_stack.is_empty() { 165 | self.print_string(string); 166 | } else { 167 | let len = string.len() as isize; 168 | self.buf.push(BufEntry { 169 | token: Token::String(string), 170 | size: len, 171 | }); 172 | self.right_total += len; 173 | self.check_stream(); 174 | } 175 | } 176 | 177 | pub fn offset(&mut self, offset: isize) { 178 | match &mut self.buf.last_mut().token { 179 | Token::Break(token) => token.offset += offset, 180 | Token::Begin(_) => {} 181 | Token::String(_) | Token::End => unreachable!(), 182 | } 183 | } 184 | 185 | pub fn end_with_max_width(&mut self, max: isize) { 186 | let mut depth = 1; 187 | for &index in self.scan_stack.iter().rev() { 188 | let entry = &self.buf[index]; 189 | match entry.token { 190 | Token::Begin(_) => { 191 | depth -= 1; 192 | if depth == 0 { 193 | if entry.size < 0 { 194 | let actual_width = entry.size + self.right_total; 195 | if actual_width > max { 196 | self.buf.push(BufEntry { 197 | token: Token::String(Cow::Borrowed("")), 198 | size: SIZE_INFINITY, 199 | }); 200 | self.right_total += SIZE_INFINITY; 201 | } 202 | } 203 | break; 204 | } 205 | } 206 | Token::End => depth += 1, 207 | Token::Break(_) => {} 208 | Token::String(_) => unreachable!(), 209 | } 210 | } 211 | self.scan_end(); 212 | } 213 | 214 | fn check_stream(&mut self) { 215 | while self.right_total - self.left_total > self.space { 216 | if *self.scan_stack.front().unwrap() == self.buf.index_of_first() { 217 | self.scan_stack.pop_front().unwrap(); 218 | self.buf.first_mut().size = SIZE_INFINITY; 219 | } 220 | 221 | self.advance_left(); 222 | 223 | if self.buf.is_empty() { 224 | break; 225 | } 226 | } 227 | } 228 | 229 | fn advance_left(&mut self) { 230 | while self.buf.first().size >= 0 { 231 | let left = self.buf.pop_first(); 232 | 233 | match left.token { 234 | Token::String(string) => { 235 | self.left_total += left.size; 236 | self.print_string(string); 237 | } 238 | Token::Break(token) => { 239 | self.left_total += token.blank_space as isize; 240 | self.print_break(token, left.size); 241 | } 242 | Token::Begin(token) => self.print_begin(token, left.size), 243 | Token::End => self.print_end(), 244 | } 245 | 246 | if self.buf.is_empty() { 247 | break; 248 | } 249 | } 250 | } 251 | 252 | fn check_stack(&mut self, mut depth: usize) { 253 | while let Some(&index) = self.scan_stack.back() { 254 | let entry = &mut self.buf[index]; 255 | match entry.token { 256 | Token::Begin(_) => { 257 | if depth == 0 { 258 | break; 259 | } 260 | self.scan_stack.pop_back().unwrap(); 261 | entry.size += self.right_total; 262 | depth -= 1; 263 | } 264 | Token::End => { 265 | self.scan_stack.pop_back().unwrap(); 266 | entry.size = 1; 267 | depth += 1; 268 | } 269 | Token::Break(_) => { 270 | self.scan_stack.pop_back().unwrap(); 271 | entry.size += self.right_total; 272 | if depth == 0 { 273 | break; 274 | } 275 | } 276 | Token::String(_) => unreachable!(), 277 | } 278 | } 279 | } 280 | 281 | fn get_top(&self) -> PrintFrame { 282 | const OUTER: PrintFrame = PrintFrame::Broken(0, Breaks::Inconsistent); 283 | self.print_stack.last().map_or(OUTER, PrintFrame::clone) 284 | } 285 | 286 | fn print_begin(&mut self, token: BeginToken, size: isize) { 287 | if cfg!(prettyplease_debug) { 288 | self.out.push(match token.breaks { 289 | Breaks::Consistent => '«', 290 | Breaks::Inconsistent => '‹', 291 | }); 292 | if cfg!(prettyplease_debug_indent) { 293 | self.out 294 | .extend(token.offset.to_string().chars().map(|ch| match ch { 295 | '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] 296 | [(ch as u8 - b'0') as usize], 297 | '-' => '₋', 298 | _ => unreachable!(), 299 | })); 300 | } 301 | } 302 | if size > self.space { 303 | self.print_stack 304 | .push(PrintFrame::Broken(self.indent, token.breaks)); 305 | self.indent = usize::try_from(self.indent as isize + token.offset).unwrap(); 306 | } else { 307 | self.print_stack.push(PrintFrame::Fits(token.breaks)); 308 | } 309 | } 310 | 311 | fn print_end(&mut self) { 312 | let breaks = match self.print_stack.pop().unwrap() { 313 | PrintFrame::Broken(indent, breaks) => { 314 | self.indent = indent; 315 | breaks 316 | } 317 | PrintFrame::Fits(breaks) => breaks, 318 | }; 319 | if cfg!(prettyplease_debug) { 320 | self.out.push(match breaks { 321 | Breaks::Consistent => '»', 322 | Breaks::Inconsistent => '›', 323 | }); 324 | } 325 | } 326 | 327 | fn print_break(&mut self, token: BreakToken, size: isize) { 328 | let fits = token.never_break 329 | || match self.get_top() { 330 | PrintFrame::Fits(..) => true, 331 | PrintFrame::Broken(.., Breaks::Consistent) => false, 332 | PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, 333 | }; 334 | if fits { 335 | self.pending_indentation += token.blank_space; 336 | self.space -= token.blank_space as isize; 337 | if let Some(no_break) = token.no_break { 338 | self.out.push(no_break); 339 | self.space -= no_break.len_utf8() as isize; 340 | } 341 | if cfg!(prettyplease_debug) { 342 | self.out.push('·'); 343 | } 344 | } else { 345 | if let Some(pre_break) = token.pre_break { 346 | self.print_indent(); 347 | self.out.push(pre_break); 348 | } 349 | if cfg!(prettyplease_debug) { 350 | self.out.push('·'); 351 | } 352 | self.out.push('\n'); 353 | let indent = self.indent as isize + token.offset; 354 | self.pending_indentation = usize::try_from(indent).unwrap(); 355 | self.space = cmp::max(MARGIN - indent, MIN_SPACE); 356 | if let Some(post_break) = token.post_break { 357 | self.print_indent(); 358 | self.out.push(post_break); 359 | self.space -= post_break.len_utf8() as isize; 360 | } 361 | } 362 | } 363 | 364 | fn print_string(&mut self, string: Cow<'static, str>) { 365 | self.print_indent(); 366 | self.out.push_str(&string); 367 | self.space -= string.len() as isize; 368 | } 369 | 370 | fn print_indent(&mut self) { 371 | self.out.reserve(self.pending_indentation); 372 | self.out 373 | .extend(iter::repeat(' ').take(self.pending_indentation)); 374 | self.pending_indentation = 0; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /examples/input.rs: -------------------------------------------------------------------------------- 1 | use crate :: cmp :: Ordering ; use crate :: fmt :: { self , Write as FmtWrite } ; use crate :: hash ; use crate :: io :: Write as IoWrite ; use crate :: mem :: transmute ; use crate :: sys :: net :: netc as c ; use crate :: sys_common :: { AsInner , FromInner , IntoInner } ; # [derive (Copy , Clone , Eq , PartialEq , Hash , PartialOrd , Ord)] pub enum IpAddr { V4 (Ipv4Addr) , V6 (Ipv6Addr) , } # [derive (Copy)] pub struct Ipv4Addr { inner : c :: in_addr , } # [derive (Copy)] pub struct Ipv6Addr { inner : c :: in6_addr , } # [derive (Copy , PartialEq , Eq , Clone , Hash , Debug)] # [non_exhaustive] pub enum Ipv6MulticastScope { InterfaceLocal , LinkLocal , RealmLocal , AdminLocal , SiteLocal , OrganizationLocal , Global , } impl IpAddr { pub const fn is_unspecified (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_unspecified () , IpAddr :: V6 (ip) => ip . is_unspecified () , } } pub const fn is_loopback (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_loopback () , IpAddr :: V6 (ip) => ip . is_loopback () , } } pub const fn is_global (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_global () , IpAddr :: V6 (ip) => ip . is_global () , } } pub const fn is_multicast (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_multicast () , IpAddr :: V6 (ip) => ip . is_multicast () , } } pub const fn is_documentation (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_documentation () , IpAddr :: V6 (ip) => ip . is_documentation () , } } pub const fn is_benchmarking (& self) -> bool { match self { IpAddr :: V4 (ip) => ip . is_benchmarking () , IpAddr :: V6 (ip) => ip . is_benchmarking () , } } pub const fn is_ipv4 (& self) -> bool { matches ! (self , IpAddr :: V4 (_)) } pub const fn is_ipv6 (& self) -> bool { matches ! (self , IpAddr :: V6 (_)) } pub const fn to_canonical (& self) -> IpAddr { match self { & v4 @ IpAddr :: V4 (_) => v4 , IpAddr :: V6 (v6) => v6 . to_canonical () , } } } impl Ipv4Addr { pub const fn new (a : u8 , b : u8 , c : u8 , d : u8) -> Ipv4Addr { Ipv4Addr { inner : c :: in_addr { s_addr : u32 :: from_ne_bytes ([a , b , c , d]) } } } pub const LOCALHOST : Self = Ipv4Addr :: new (127 , 0 , 0 , 1) ; # [doc (alias = "INADDR_ANY")] pub const UNSPECIFIED : Self = Ipv4Addr :: new (0 , 0 , 0 , 0) ; pub const BROADCAST : Self = Ipv4Addr :: new (255 , 255 , 255 , 255) ; pub const fn octets (& self) -> [u8 ; 4] { self . inner . s_addr . to_ne_bytes () } pub const fn is_unspecified (& self) -> bool { self . inner . s_addr == 0 } pub const fn is_loopback (& self) -> bool { self . octets () [0] == 127 } pub const fn is_private (& self) -> bool { match self . octets () { [10 , ..] => true , [172 , b , ..] if b >= 16 && b <= 31 => true , [192 , 168 , ..] => true , _ => false , } } pub const fn is_link_local (& self) -> bool { matches ! (self . octets () , [169 , 254 , ..]) } pub const fn is_global (& self) -> bool { if u32 :: from_be_bytes (self . octets ()) == 0xc0000009 || u32 :: from_be_bytes (self . octets ()) == 0xc000000a { return true ; } ! self . is_private () && ! self . is_loopback () && ! self . is_link_local () && ! self . is_broadcast () && ! self . is_documentation () && ! self . is_shared () && ! (self . octets () [0] == 192 && self . octets () [1] == 0 && self . octets () [2] == 0) && ! self . is_reserved () && ! self . is_benchmarking () && self . octets () [0] != 0 } pub const fn is_shared (& self) -> bool { self . octets () [0] == 100 && (self . octets () [1] & 0b1100_0000 == 0b0100_0000) } pub const fn is_benchmarking (& self) -> bool { self . octets () [0] == 198 && (self . octets () [1] & 0xfe) == 18 } pub const fn is_reserved (& self) -> bool { self . octets () [0] & 240 == 240 && ! self . is_broadcast () } pub const fn is_multicast (& self) -> bool { self . octets () [0] >= 224 && self . octets () [0] <= 239 } pub const fn is_broadcast (& self) -> bool { u32 :: from_be_bytes (self . octets ()) == u32 :: from_be_bytes (Self :: BROADCAST . octets ()) } pub const fn is_documentation (& self) -> bool { matches ! (self . octets () , [192 , 0 , 2 , _] | [198 , 51 , 100 , _] | [203 , 0 , 113 , _]) } pub const fn to_ipv6_compatible (& self) -> Ipv6Addr { let [a , b , c , d] = self . octets () ; Ipv6Addr { inner : c :: in6_addr { s6_addr : [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , a , b , c , d] } , } } pub const fn to_ipv6_mapped (& self) -> Ipv6Addr { let [a , b , c , d] = self . octets () ; Ipv6Addr { inner : c :: in6_addr { s6_addr : [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xFF , 0xFF , a , b , c , d] } , } } } impl fmt :: Display for IpAddr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { match self { IpAddr :: V4 (ip) => ip . fmt (fmt) , IpAddr :: V6 (ip) => ip . fmt (fmt) , } } } impl fmt :: Debug for IpAddr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { fmt :: Display :: fmt (self , fmt) } } impl From < Ipv4Addr > for IpAddr { fn from (ipv4 : Ipv4Addr) -> IpAddr { IpAddr :: V4 (ipv4) } } impl From < Ipv6Addr > for IpAddr { fn from (ipv6 : Ipv6Addr) -> IpAddr { IpAddr :: V6 (ipv6) } } impl fmt :: Display for Ipv4Addr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { let octets = self . octets () ; if fmt . precision () . is_none () && fmt . width () . is_none () { write ! (fmt , "{}.{}.{}.{}" , octets [0] , octets [1] , octets [2] , octets [3]) } else { const IPV4_BUF_LEN : usize = 15 ; let mut buf = [0u8 ; IPV4_BUF_LEN] ; let mut buf_slice = & mut buf [..] ; write ! (buf_slice , "{}.{}.{}.{}" , octets [0] , octets [1] , octets [2] , octets [3]) . unwrap () ; let len = IPV4_BUF_LEN - buf_slice . len () ; let buf = unsafe { crate :: str :: from_utf8_unchecked (& buf [.. len]) } ; fmt . pad (buf) } } } impl fmt :: Debug for Ipv4Addr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { fmt :: Display :: fmt (self , fmt) } } impl Clone for Ipv4Addr { fn clone (& self) -> Ipv4Addr { * self } } impl PartialEq for Ipv4Addr { fn eq (& self , other : & Ipv4Addr) -> bool { self . inner . s_addr == other . inner . s_addr } } impl PartialEq < Ipv4Addr > for IpAddr { fn eq (& self , other : & Ipv4Addr) -> bool { match self { IpAddr :: V4 (v4) => v4 == other , IpAddr :: V6 (_) => false , } } } impl PartialEq < IpAddr > for Ipv4Addr { fn eq (& self , other : & IpAddr) -> bool { match other { IpAddr :: V4 (v4) => self == v4 , IpAddr :: V6 (_) => false , } } } impl Eq for Ipv4Addr { } impl hash :: Hash for Ipv4Addr { fn hash < H : hash :: Hasher > (& self , s : & mut H) { { self . inner . s_addr } . hash (s) } } impl PartialOrd for Ipv4Addr { fn partial_cmp (& self , other : & Ipv4Addr) -> Option < Ordering > { Some (self . cmp (other)) } } impl PartialOrd < Ipv4Addr > for IpAddr { fn partial_cmp (& self , other : & Ipv4Addr) -> Option < Ordering > { match self { IpAddr :: V4 (v4) => v4 . partial_cmp (other) , IpAddr :: V6 (_) => Some (Ordering :: Greater) , } } } impl PartialOrd < IpAddr > for Ipv4Addr { fn partial_cmp (& self , other : & IpAddr) -> Option < Ordering > { match other { IpAddr :: V4 (v4) => self . partial_cmp (v4) , IpAddr :: V6 (_) => Some (Ordering :: Less) , } } } impl Ord for Ipv4Addr { fn cmp (& self , other : & Ipv4Addr) -> Ordering { u32 :: from_be (self . inner . s_addr) . cmp (& u32 :: from_be (other . inner . s_addr)) } } impl IntoInner < c :: in_addr > for Ipv4Addr { fn into_inner (self) -> c :: in_addr { self . inner } } impl From < Ipv4Addr > for u32 { fn from (ip : Ipv4Addr) -> u32 { let ip = ip . octets () ; u32 :: from_be_bytes (ip) } } impl From < u32 > for Ipv4Addr { fn from (ip : u32) -> Ipv4Addr { Ipv4Addr :: from (ip . to_be_bytes ()) } } impl From < [u8 ; 4] > for Ipv4Addr { fn from (octets : [u8 ; 4]) -> Ipv4Addr { Ipv4Addr :: new (octets [0] , octets [1] , octets [2] , octets [3]) } } impl From < [u8 ; 4] > for IpAddr { fn from (octets : [u8 ; 4]) -> IpAddr { IpAddr :: V4 (Ipv4Addr :: from (octets)) } } impl Ipv6Addr { pub const fn new (a : u16 , b : u16 , c : u16 , d : u16 , e : u16 , f : u16 , g : u16 , h : u16) -> Ipv6Addr { let addr16 = [a . to_be () , b . to_be () , c . to_be () , d . to_be () , e . to_be () , f . to_be () , g . to_be () , h . to_be () ,] ; Ipv6Addr { inner : c :: in6_addr { s6_addr : unsafe { transmute :: < _ , [u8 ; 16] > (addr16) } , } , } } pub const LOCALHOST : Self = Ipv6Addr :: new (0 , 0 , 0 , 0 , 0 , 0 , 0 , 1) ; pub const UNSPECIFIED : Self = Ipv6Addr :: new (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0) ; pub const fn segments (& self) -> [u16 ; 8] { let [a , b , c , d , e , f , g , h] = unsafe { transmute :: < _ , [u16 ; 8] > (self . inner . s6_addr) } ; [u16 :: from_be (a) , u16 :: from_be (b) , u16 :: from_be (c) , u16 :: from_be (d) , u16 :: from_be (e) , u16 :: from_be (f) , u16 :: from_be (g) , u16 :: from_be (h) ,] } pub const fn is_unspecified (& self) -> bool { u128 :: from_be_bytes (self . octets ()) == u128 :: from_be_bytes (Ipv6Addr :: UNSPECIFIED . octets ()) } pub const fn is_loopback (& self) -> bool { u128 :: from_be_bytes (self . octets ()) == u128 :: from_be_bytes (Ipv6Addr :: LOCALHOST . octets ()) } pub const fn is_global (& self) -> bool { match self . multicast_scope () { Some (Ipv6MulticastScope :: Global) => true , None => self . is_unicast_global () , _ => false , } } pub const fn is_unique_local (& self) -> bool { (self . segments () [0] & 0xfe00) == 0xfc00 } pub const fn is_unicast (& self) -> bool { ! self . is_multicast () } pub const fn is_unicast_link_local (& self) -> bool { (self . segments () [0] & 0xffc0) == 0xfe80 } pub const fn is_documentation (& self) -> bool { (self . segments () [0] == 0x2001) && (self . segments () [1] == 0xdb8) } pub const fn is_benchmarking (& self) -> bool { (self . segments () [0] == 0x2001) && (self . segments () [1] == 0x2) && (self . segments () [2] == 0) } pub const fn is_unicast_global (& self) -> bool { self . is_unicast () && ! self . is_loopback () && ! self . is_unicast_link_local () && ! self . is_unique_local () && ! self . is_unspecified () && ! self . is_documentation () } pub const fn multicast_scope (& self) -> Option < Ipv6MulticastScope > { if self . is_multicast () { match self . segments () [0] & 0x000f { 1 => Some (Ipv6MulticastScope :: InterfaceLocal) , 2 => Some (Ipv6MulticastScope :: LinkLocal) , 3 => Some (Ipv6MulticastScope :: RealmLocal) , 4 => Some (Ipv6MulticastScope :: AdminLocal) , 5 => Some (Ipv6MulticastScope :: SiteLocal) , 8 => Some (Ipv6MulticastScope :: OrganizationLocal) , 14 => Some (Ipv6MulticastScope :: Global) , _ => None , } } else { None } } pub const fn is_multicast (& self) -> bool { (self . segments () [0] & 0xff00) == 0xff00 } pub const fn to_ipv4_mapped (& self) -> Option < Ipv4Addr > { match self . octets () { [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xff , 0xff , a , b , c , d] => { Some (Ipv4Addr :: new (a , b , c , d)) } _ => None , } } pub const fn to_ipv4 (& self) -> Option < Ipv4Addr > { if let [0 , 0 , 0 , 0 , 0 , 0 | 0xffff , ab , cd] = self . segments () { let [a , b] = ab . to_be_bytes () ; let [c , d] = cd . to_be_bytes () ; Some (Ipv4Addr :: new (a , b , c , d)) } else { None } } pub const fn to_canonical (& self) -> IpAddr { if let Some (mapped) = self . to_ipv4_mapped () { return IpAddr :: V4 (mapped) ; } IpAddr :: V6 (* self) } pub const fn octets (& self) -> [u8 ; 16] { self . inner . s6_addr } } impl fmt :: Display for Ipv6Addr { fn fmt (& self , f : & mut fmt :: Formatter < '_ >) -> fmt :: Result { if f . precision () . is_none () && f . width () . is_none () { let segments = self . segments () ; if self . is_unspecified () { f . write_str ("::") } else if self . is_loopback () { f . write_str ("::1") } else if let Some (ipv4) = self . to_ipv4 () { match segments [5] { 0 => write ! (f , "::{}" , ipv4) , 0xffff => write ! (f , "::ffff:{}" , ipv4) , _ => unreachable ! () , } } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } } else { const IPV6_BUF_LEN : usize = (4 * 8) + 7 ; let mut buf = [0u8 ; IPV6_BUF_LEN] ; let mut buf_slice = & mut buf [..] ; write ! (buf_slice , "{}" , self) . unwrap () ; let len = IPV6_BUF_LEN - buf_slice . len () ; let buf = unsafe { crate :: str :: from_utf8_unchecked (& buf [.. len]) } ; f . pad (buf) } } } impl fmt :: Debug for Ipv6Addr { fn fmt (& self , fmt : & mut fmt :: Formatter < '_ >) -> fmt :: Result { fmt :: Display :: fmt (self , fmt) } } impl Clone for Ipv6Addr { fn clone (& self) -> Ipv6Addr { * self } } impl PartialEq for Ipv6Addr { fn eq (& self , other : & Ipv6Addr) -> bool { self . inner . s6_addr == other . inner . s6_addr } } impl PartialEq < IpAddr > for Ipv6Addr { fn eq (& self , other : & IpAddr) -> bool { match other { IpAddr :: V4 (_) => false , IpAddr :: V6 (v6) => self == v6 , } } } impl PartialEq < Ipv6Addr > for IpAddr { fn eq (& self , other : & Ipv6Addr) -> bool { match self { IpAddr :: V4 (_) => false , IpAddr :: V6 (v6) => v6 == other , } } } impl Eq for Ipv6Addr { } impl hash :: Hash for Ipv6Addr { fn hash < H : hash :: Hasher > (& self , s : & mut H) { self . inner . s6_addr . hash (s) } } impl PartialOrd for Ipv6Addr { fn partial_cmp (& self , other : & Ipv6Addr) -> Option < Ordering > { Some (self . cmp (other)) } } impl PartialOrd < Ipv6Addr > for IpAddr { fn partial_cmp (& self , other : & Ipv6Addr) -> Option < Ordering > { match self { IpAddr :: V4 (_) => Some (Ordering :: Less) , IpAddr :: V6 (v6) => v6 . partial_cmp (other) , } } } impl PartialOrd < IpAddr > for Ipv6Addr { fn partial_cmp (& self , other : & IpAddr) -> Option < Ordering > { match other { IpAddr :: V4 (_) => Some (Ordering :: Greater) , IpAddr :: V6 (v6) => self . partial_cmp (v6) , } } } impl Ord for Ipv6Addr { fn cmp (& self , other : & Ipv6Addr) -> Ordering { self . segments () . cmp (& other . segments ()) } } impl AsInner < c :: in6_addr > for Ipv6Addr { fn as_inner (& self) -> & c :: in6_addr { & self . inner } } impl FromInner < c :: in6_addr > for Ipv6Addr { fn from_inner (addr : c :: in6_addr) -> Ipv6Addr { Ipv6Addr { inner : addr } } } impl From < Ipv6Addr > for u128 { fn from (ip : Ipv6Addr) -> u128 { let ip = ip . octets () ; u128 :: from_be_bytes (ip) } } impl From < u128 > for Ipv6Addr { fn from (ip : u128) -> Ipv6Addr { Ipv6Addr :: from (ip . to_be_bytes ()) } } impl From < [u8 ; 16] > for Ipv6Addr { fn from (octets : [u8 ; 16]) -> Ipv6Addr { let inner = c :: in6_addr { s6_addr : octets } ; Ipv6Addr :: from_inner (inner) } } impl From < [u16 ; 8] > for Ipv6Addr { fn from (segments : [u16 ; 8]) -> Ipv6Addr { let [a , b , c , d , e , f , g , h] = segments ; Ipv6Addr :: new (a , b , c , d , e , f , g , h) } } impl From < [u8 ; 16] > for IpAddr { fn from (octets : [u8 ; 16]) -> IpAddr { IpAddr :: V6 (Ipv6Addr :: from (octets)) } } impl From < [u16 ; 8] > for IpAddr { fn from (segments : [u16 ; 8]) -> IpAddr { IpAddr :: V6 (Ipv6Addr :: from (segments)) } } 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prettier Please 2 | ===================== 3 | 4 | 5 | 6 | [github](https://github.com/dtolnay/prettyplease) 7 | [crates.io](https://crates.io/crates/prettyplease) 8 | [docs.rs](https://docs.rs/prettyplease) 9 | [build status](https://github.com/dtolnay/prettyplease/actions?query=branch%3Amaster) 10 | 11 | A minimal `syn` syntax tree pretty-printer with hooks for formatting procedural macros. 12 | 13 | This crate provides functionality to pretty-print your procedural macros and any captured Rust code. 14 | 15 | This is intended for macros like Dioxus' `rsx!` and Yew's `html!` to properly format their captured Rust code. 16 | 17 | This crate diverges from Dtonlay's prettyplease in that it is trying to be as close to Rustfmt as possible instead of "good enough". 18 | 19 | Prettier-Please is designed for use in LSPs to make use of DSLs in Rust easier. It is used in Dioxus autoformatting logic to support nested calls. 20 | 21 |
22 | 23 | ## Overview 24 | 25 | This is a pretty-printer to turn a `syn` syntax tree into a `String` of 26 | well-formatted source code. In contrast to rustfmt, this library is intended to 27 | be suitable for arbitrary generated code. 28 | 29 | Rustfmt prioritizes high-quality output that is impeccable enough that you'd be 30 | comfortable spending your career staring at its output — but that means 31 | some heavyweight algorithms, and it has a tendency to bail out on code that is 32 | hard to format (for example [rustfmt#3697], and there are dozens more issues 33 | like it). That's not necessarily a big deal for human-generated code because 34 | when code gets highly nested, the human will naturally be inclined to refactor 35 | into more easily formattable code. But for generated code, having the formatter 36 | just give up leaves it totally unreadable. 37 | 38 | [rustfmt#3697]: https://github.com/rust-lang/rustfmt/issues/3697 39 | 40 | This library is designed using the simplest possible algorithm and data 41 | structures that can deliver about 95% of the quality of rustfmt-formatted 42 | output. In my experience testing real-world code, approximately 97-98% of output 43 | lines come out identical between rustfmt's formatting and this crate's. The rest 44 | have slightly different linebreak decisions, but still clearly follow the 45 | dominant modern Rust style. 46 | 47 | The tradeoffs made by this crate are a good fit for generated code that you will 48 | *not* spend your career staring at. For example, the output of `bindgen`, or the 49 | output of `cargo-expand`. In those cases it's more important that the whole 50 | thing be formattable without the formatter giving up, than that it be flawless. 51 | 52 |
53 | 54 | ## Feature matrix 55 | 56 | Here are a few superficial comparisons of this crate against the AST 57 | pretty-printer built into rustc, and rustfmt. The sections below go into more 58 | detail comparing the output of each of these libraries. 59 | 60 | | | prettyplease | rustc | rustfmt | 61 | | ---------------------------------------------------------------------- | ------------ | -------- | -------- | 62 | | non-pathological behavior on big or generated code | ✔️ | ❌ | ❌ | 63 | | idiomatic modern formatting ("locally indistinguishable from rustfmt") | ✔️ | ❌ | ✔️ | 64 | | throughput | 60 MB/s | 39 MB/s | 2.8 MB/s | 65 | | number of dependencies | 3 | 72 | 66 | 66 | | compile time including dependencies | 2.4 sec | 23.1 sec | 29.8 sec | 67 | | buildable using a stable Rust compiler | ✔️ | ❌ | ❌ | 68 | | published to crates.io | ✔️ | ❌ | ❌ | 69 | | extensively configurable output | ❌ | ❌ | ✔️ | 70 | | intended to accommodate hand-maintained source code | ❌ | ❌ | ✔️ | 71 | 72 |
73 | 74 | ## Comparison to rustfmt 75 | 76 | - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) 77 | - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) 78 | - [output.rustfmt.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustfmt.rs) 79 | 80 | If you weren't told which output file is which, it would be practically 81 | impossible to tell — **except** for line 435 in the rustfmt output, which 82 | is more than 1000 characters long because rustfmt just gave up formatting that 83 | part of the file: 84 | 85 | ```rust 86 | match segments[5] { 87 | 0 => write!(f, "::{}", ipv4), 88 | 0xffff => write!(f, "::ffff:{}", ipv4), 89 | _ => unreachable!(), 90 | } 91 | } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } 92 | } else { 93 | const IPV6_BUF_LEN: usize = (4 * 8) + 7; 94 | let mut buf = [0u8; IPV6_BUF_LEN]; 95 | let mut buf_slice = &mut buf[..]; 96 | ``` 97 | 98 | This is a pretty typical manifestation of rustfmt bailing out in generated code 99 | — a chunk of the input ends up on one line. The other manifestation is 100 | that you're working on some code, running rustfmt on save like a conscientious 101 | developer, but after a while notice it isn't doing anything. You introduce an 102 | intentional formatting issue, like a stray indent or semicolon, and run rustfmt 103 | to check your suspicion. Nope, it doesn't get cleaned up — rustfmt is just 104 | not formatting the part of the file you are working on. 105 | 106 | The prettyplease library is designed to have no pathological cases that force a 107 | bail out; the entire input you give it will get formatted in some "good enough" 108 | form. 109 | 110 | Separately, rustfmt can be problematic to integrate into projects. It's written 111 | using rustc's internal syntax tree, so it can't be built by a stable compiler. 112 | Its releases are not regularly published to crates.io, so in Cargo builds you'd 113 | need to depend on it as a git dependency, which precludes publishing your crate 114 | to crates.io also. You can shell out to a `rustfmt` binary, but that'll be 115 | whatever rustfmt version is installed on each developer's system (if any), which 116 | can lead to spurious diffs in checked-in generated code formatted by different 117 | versions. In contrast prettyplease is designed to be easy to pull in as a 118 | library, and compiles fast. 119 | 120 |
121 | 122 | ## Comparison to rustc_ast_pretty 123 | 124 | - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) 125 | - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) 126 | - [output.rustc.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustc.rs) 127 | 128 | This is the pretty-printer that gets used when rustc prints source code, such as 129 | `rustc -Zunpretty=expanded`. It's used also by the standard library's 130 | `stringify!` when stringifying an interpolated macro_rules AST fragment, like an 131 | $:expr, and transitively by `dbg!` and many macros in the ecosystem. 132 | 133 | Rustc's formatting is mostly okay, but does not hew closely to the dominant 134 | contemporary style of Rust formatting. Some things wouldn't ever be written on 135 | one line, like this `match` expression, and certainly not with a comma in front 136 | of the closing brace: 137 | 138 | ```rust 139 | fn eq(&self, other: &IpAddr) -> bool { 140 | match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } 141 | } 142 | ``` 143 | 144 | Some places use non-multiple-of-4 indentation, which is definitely not the norm: 145 | 146 | ```rust 147 | pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { 148 | let [a, b, c, d] = self.octets(); 149 | Ipv6Addr{inner: 150 | c::in6_addr{s6_addr: 151 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 152 | 0xFF, a, b, c, d],},} 153 | } 154 | ``` 155 | 156 | And although there isn't an egregious example of it in the link because the 157 | input code is pretty tame, in general rustc_ast_pretty has pathological behavior 158 | on generated code. It has a tendency to use excessive horizontal indentation and 159 | rapidly run out of width: 160 | 161 | ```rust 162 | ::std::io::_print(::core::fmt::Arguments::new_v1(&[""], 163 | &match (&msg,) { 164 | _args => 165 | [::core::fmt::ArgumentV1::new(_args.0, 166 | ::core::fmt::Display::fmt)], 167 | })); 168 | ``` 169 | 170 | The snippets above are clearly different from modern rustfmt style. In contrast, 171 | prettyplease is designed to have output that is practically indistinguishable 172 | from rustfmt-formatted code. 173 | 174 |
175 | 176 | ## Example 177 | 178 | ```rust 179 | // [dependencies] 180 | // prettyplease = "0.1" 181 | // syn = { version = "1", default-features = false, features = ["full", "parsing"] } 182 | 183 | const INPUT: &str = stringify! { 184 | use crate::{ 185 | lazy::{Lazy, SyncLazy, SyncOnceCell}, panic, 186 | sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, 187 | mpsc::channel, Mutex, }, 188 | thread, 189 | }; 190 | impl Into for T where U: From { 191 | fn into(self) -> U { U::from(self) } 192 | } 193 | }; 194 | 195 | fn main() { 196 | let syntax_tree = syn::parse_file(INPUT).unwrap(); 197 | let formatted = prettyplease::unparse(&syntax_tree); 198 | print!("{}", formatted); 199 | } 200 | ``` 201 | 202 |
203 | 204 | ## Algorithm notes 205 | 206 | The approach and terminology used in the implementation are derived from [*Derek 207 | C. Oppen, "Pretty Printing" (1979)*][paper], on which rustc_ast_pretty is also 208 | based, and from rustc_ast_pretty's implementation written by Graydon Hoare in 209 | 2011 (and modernized over the years by dozens of volunteer maintainers). 210 | 211 | [paper]: http://i.stanford.edu/pub/cstr/reports/cs/tr/79/770/CS-TR-79-770.pdf 212 | 213 | The paper describes two language-agnostic interacting procedures `Scan()` and 214 | `Print()`. Language-specific code decomposes an input data structure into a 215 | stream of `string` and `break` tokens, and `begin` and `end` tokens for 216 | grouping. Each `begin`–`end` range may be identified as either "consistent 217 | breaking" or "inconsistent breaking". If a group is consistently breaking, then 218 | if the whole contents do not fit on the line, *every* `break` token in the group 219 | will receive a linebreak. This is appropriate, for example, for Rust struct 220 | literals, or arguments of a function call. If a group is inconsistently 221 | breaking, then the `string` tokens in the group are greedily placed on the line 222 | until out of space, and linebroken only at those `break` tokens for which the 223 | next string would not fit. For example, this is appropriate for the contents of 224 | a braced `use` statement in Rust. 225 | 226 | Scan's job is to efficiently accumulate sizing information about groups and 227 | breaks. For every `begin` token we compute the distance to the matched `end` 228 | token, and for every `break` we compute the distance to the next `break`. The 229 | algorithm uses a ringbuffer to hold tokens whose size is not yet ascertained. 230 | The maximum size of the ringbuffer is bounded by the target line length and does 231 | not grow indefinitely, regardless of deep nesting in the input stream. That's 232 | because once a group is sufficiently big, the precise size can no longer make a 233 | difference to linebreak decisions and we can effectively treat it as "infinity". 234 | 235 | Print's job is to use the sizing information to efficiently assign a "broken" or 236 | "not broken" status to every `begin` token. At that point the output is easily 237 | constructed by concatenating `string` tokens and breaking at `break` tokens 238 | contained within a broken group. 239 | 240 | Leveraging these primitives (i.e. cleverly placing the all-or-nothing consistent 241 | breaks and greedy inconsistent breaks) to yield rustfmt-compatible formatting 242 | for all of Rust's syntax tree nodes is a fun challenge. 243 | 244 | Here is a visualization of some Rust tokens fed into the pretty printing 245 | algorithm. Consistently breaking `begin`—`end` pairs are represented by 246 | `«`⁠`»`, inconsistently breaking by `‹`⁠`›`, `break` by `·`, and the 247 | rest of the non-whitespace are `string`. 248 | 249 | ```text 250 | use crate::«{· 251 | ‹ lazy::«{·‹Lazy,· SyncLazy,· SyncOnceCell›·}»,· 252 | panic,· 253 | sync::«{· 254 | ‹ atomic::«{·‹AtomicUsize,· Ordering::SeqCst›·}»,· 255 | mpsc::channel,· Mutex›,· 256 | }»,· 257 | thread›,· 258 | }»;· 259 | «‹«impl<«·T‹›,· U‹›·»>» Into<«·U·»>· for T›· 260 | where· 261 | U:‹ From<«·T·»>›,· 262 | {· 263 | « fn into(·«·self·») -> U {· 264 | ‹ U::from(«·self·»)›· 265 | » }· 266 | »}· 267 | ``` 268 | 269 | The algorithm described in the paper is not quite sufficient for producing 270 | well-formatted Rust code that is locally indistinguishable from rustfmt's style. 271 | The reason is that in the paper, the complete non-whitespace contents are 272 | assumed to be independent of linebreak decisions, with Scan and Print being only 273 | in control of the whitespace (spaces and line breaks). In Rust as idiomatically 274 | formattted by rustfmt, that is not the case. Trailing commas are one example; 275 | the punctuation is only known *after* the broken vs non-broken status of the 276 | surrounding group is known: 277 | 278 | ```rust 279 | let _ = Struct { x: 0, y: true }; 280 | 281 | let _ = Struct { 282 | x: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, 283 | y: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, //<- trailing comma if the expression wrapped 284 | }; 285 | ``` 286 | 287 | The formatting of `match` expressions is another case; we want small arms on the 288 | same line as the pattern, and big arms wrapped in a brace. The presence of the 289 | brace punctuation, comma, and semicolon are all dependent on whether the arm 290 | fits on the line: 291 | 292 | ```rust 293 | match total_nanos.checked_add(entry.nanos as u64) { 294 | Some(n) => tmp = n, //<- small arm, inline with comma 295 | None => { 296 | total_secs = total_secs 297 | .checked_add(total_nanos / NANOS_PER_SEC as u64) 298 | .expect("overflow in iter::sum over durations"); 299 | } //<- big arm, needs brace added, and also semicolon^ 300 | } 301 | ``` 302 | 303 | The printing algorithm implementation in this crate accommodates all of these 304 | situations with conditional punctuation tokens whose selection can be deferred 305 | and populated after it's known that the group is or is not broken. 306 | 307 |
308 | 309 | #### License 310 | 311 | 312 | Licensed under either of Apache License, Version 313 | 2.0 or MIT license at your option. 314 | 315 | 316 |
317 | 318 | 319 | Unless you explicitly state otherwise, any contribution intentionally submitted 320 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 321 | be dual licensed as above, without any additional terms or conditions. 322 | 323 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! [![github]](https://github.com/dtolnay/prettyplease) [![crates-io]](https://crates.io/crates/prettyplease) [![docs-rs]](https://docs.rs/prettyplease) 2 | //! 3 | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github 4 | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust 5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs 6 | //! 7 | //!
8 | //! 9 | //! **prettyplease::unparse** — a minimal `syn` syntax tree pretty-printer 10 | //! 11 | //!
12 | //! 13 | //! # Overview 14 | //! 15 | //! This is a pretty-printer to turn a `syn` syntax tree into a `String` of 16 | //! well-formatted source code. In contrast to rustfmt, this library is intended 17 | //! to be suitable for arbitrary generated code. 18 | //! 19 | //! Rustfmt prioritizes high-quality output that is impeccable enough that you'd 20 | //! be comfortable spending your career staring at its output — but that 21 | //! means some heavyweight algorithms, and it has a tendency to bail out on code 22 | //! that is hard to format (for example [rustfmt#3697], and there are dozens 23 | //! more issues like it). That's not necessarily a big deal for human-generated 24 | //! code because when code gets highly nested, the human will naturally be 25 | //! inclined to refactor into more easily formattable code. But for generated 26 | //! code, having the formatter just give up leaves it totally unreadable. 27 | //! 28 | //! [rustfmt#3697]: https://github.com/rust-lang/rustfmt/issues/3697 29 | //! 30 | //! This library is designed using the simplest possible algorithm and data 31 | //! structures that can deliver about 95% of the quality of rustfmt-formatted 32 | //! output. In my experience testing real-world code, approximately 97-98% of 33 | //! output lines come out identical between rustfmt's formatting and this 34 | //! crate's. The rest have slightly different linebreak decisions, but still 35 | //! clearly follow the dominant modern Rust style. 36 | //! 37 | //! The tradeoffs made by this crate are a good fit for generated code that you 38 | //! will *not* spend your career staring at. For example, the output of 39 | //! `bindgen`, or the output of `cargo-expand`. In those cases it's more 40 | //! important that the whole thing be formattable without the formatter giving 41 | //! up, than that it be flawless. 42 | //! 43 | //!
44 | //! 45 | //! # Feature matrix 46 | //! 47 | //! Here are a few superficial comparisons of this crate against the AST 48 | //! pretty-printer built into rustc, and rustfmt. The sections below go into 49 | //! more detail comparing the output of each of these libraries. 50 | //! 51 | //! | | prettyplease | rustc | rustfmt | 52 | //! |:---|:---:|:---:|:---:| 53 | //! | non-pathological behavior on big or generated code | 💚 | ❌ | ❌ | 54 | //! | idiomatic modern formatting ("locally indistinguishable from rustfmt") | 💚 | ❌ | 💚 | 55 | //! | throughput | 60 MB/s | 39 MB/s | 2.8 MB/s | 56 | //! | number of dependencies | 3 | 72 | 66 | 57 | //! | compile time including dependencies | 2.4 sec | 23.1 sec | 29.8 sec | 58 | //! | buildable using a stable Rust compiler | 💚 | ❌ | ❌ | 59 | //! | published to crates.io | 💚 | ❌ | ❌ | 60 | //! | extensively configurable output | ❌ | ❌ | 💚 | 61 | //! | intended to accommodate hand-maintained source code | ❌ | ❌ | 💚 | 62 | //! 63 | //!
64 | //! 65 | //! # Comparison to rustfmt 66 | //! 67 | //! - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) 68 | //! - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) 69 | //! - [output.rustfmt.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustfmt.rs) 70 | //! 71 | //! If you weren't told which output file is which, it would be practically 72 | //! impossible to tell — **except** for line 435 in the rustfmt output, 73 | //! which is more than 1000 characters long because rustfmt just gave up 74 | //! formatting that part of the file: 75 | //! 76 | //! ``` 77 | //! # const _: &str = stringify! {{{ 78 | //! match segments[5] { 79 | //! 0 => write!(f, "::{}", ipv4), 80 | //! 0xffff => write!(f, "::ffff:{}", ipv4), 81 | //! _ => unreachable!(), 82 | //! } 83 | //! } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } 84 | //! } else { 85 | //! const IPV6_BUF_LEN: usize = (4 * 8) + 7; 86 | //! let mut buf = [0u8; IPV6_BUF_LEN]; 87 | //! let mut buf_slice = &mut buf[..]; 88 | //! # }}; 89 | //! ``` 90 | //! 91 | //! This is a pretty typical manifestation of rustfmt bailing out in generated 92 | //! code — a chunk of the input ends up on one line. The other 93 | //! manifestation is that you're working on some code, running rustfmt on save 94 | //! like a conscientious developer, but after a while notice it isn't doing 95 | //! anything. You introduce an intentional formatting issue, like a stray indent 96 | //! or semicolon, and run rustfmt to check your suspicion. Nope, it doesn't get 97 | //! cleaned up — rustfmt is just not formatting the part of the file you 98 | //! are working on. 99 | //! 100 | //! The prettyplease library is designed to have no pathological cases that 101 | //! force a bail out; the entire input you give it will get formatted in some 102 | //! "good enough" form. 103 | //! 104 | //! Separately, rustfmt can be problematic to integrate into projects. It's 105 | //! written using rustc's internal syntax tree, so it can't be built by a stable 106 | //! compiler. Its releases are not regularly published to crates.io, so in Cargo 107 | //! builds you'd need to depend on it as a git dependency, which precludes 108 | //! publishing your crate to crates.io also. You can shell out to a `rustfmt` 109 | //! binary, but that'll be whatever rustfmt version is installed on each 110 | //! developer's system (if any), which can lead to spurious diffs in checked-in 111 | //! generated code formatted by different versions. In contrast prettyplease is 112 | //! designed to be easy to pull in as a library, and compiles fast. 113 | //! 114 | //!
115 | //! 116 | //! # Comparison to rustc_ast_pretty 117 | //! 118 | //! - [input.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/input.rs) 119 | //! - [output.prettyplease.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.prettyplease.rs) 120 | //! - [output.rustc.rs](https://github.com/dtolnay/prettyplease/blob/0.1.0/examples/output.rustc.rs) 121 | //! 122 | //! This is the pretty-printer that gets used when rustc prints source code, 123 | //! such as `rustc -Zunpretty=expanded`. It's used also by the standard 124 | //! library's `stringify!` when stringifying an interpolated macro_rules AST 125 | //! fragment, like an $:expr, and transitively by `dbg!` and many macros in the 126 | //! ecosystem. 127 | //! 128 | //! Rustc's formatting is mostly okay, but does not hew closely to the dominant 129 | //! contemporary style of Rust formatting. Some things wouldn't ever be written 130 | //! on one line, like this `match` expression, and certainly not with a comma in 131 | //! front of the closing brace: 132 | //! 133 | //! ``` 134 | //! # const _: &str = stringify! { 135 | //! fn eq(&self, other: &IpAddr) -> bool { 136 | //! match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } 137 | //! } 138 | //! # }; 139 | //! ``` 140 | //! 141 | //! Some places use non-multiple-of-4 indentation, which is definitely not the 142 | //! norm: 143 | //! 144 | //! ``` 145 | //! # const _: &str = stringify! { 146 | //! pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { 147 | //! let [a, b, c, d] = self.octets(); 148 | //! Ipv6Addr{inner: 149 | //! c::in6_addr{s6_addr: 150 | //! [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 151 | //! 0xFF, a, b, c, d],},} 152 | //! } 153 | //! # }; 154 | //! ``` 155 | //! 156 | //! And although there isn't an egregious example of it in the link because the 157 | //! input code is pretty tame, in general rustc_ast_pretty has pathological 158 | //! behavior on generated code. It has a tendency to use excessive horizontal 159 | //! indentation and rapidly run out of width: 160 | //! 161 | //! ``` 162 | //! # const _: &str = stringify! { 163 | //! ::std::io::_print(::core::fmt::Arguments::new_v1(&[""], 164 | //! &match (&msg,) { 165 | //! _args => 166 | //! [::core::fmt::ArgumentV1::new(_args.0, 167 | //! ::core::fmt::Display::fmt)], 168 | //! })); 169 | //! # }; 170 | //! ``` 171 | //! 172 | //! The snippets above are clearly different from modern rustfmt style. In 173 | //! contrast, prettyplease is designed to have output that is practically 174 | //! indistinguishable from rustfmt-formatted code. 175 | //! 176 | //!
177 | //! 178 | //! # Example 179 | //! 180 | //! ``` 181 | //! // [dependencies] 182 | //! // prettyplease = "0.2" 183 | //! // syn = { version = "2", default-features = false, features = ["full", "parsing"] } 184 | //! 185 | //! const INPUT: &str = stringify! { 186 | //! use crate::{ 187 | //! lazy::{Lazy, SyncLazy, SyncOnceCell}, panic, 188 | //! sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, 189 | //! mpsc::channel, Mutex, }, 190 | //! thread, 191 | //! }; 192 | //! impl Into for T where U: From { 193 | //! fn into(self) -> U { U::from(self) } 194 | //! } 195 | //! }; 196 | //! 197 | //! fn main() { 198 | //! let syntax_tree = syn::parse_file(INPUT).unwrap(); 199 | //! let formatted = prettyplease::unparse(&syntax_tree); 200 | //! print!("{}", formatted); 201 | //! } 202 | //! ``` 203 | //! 204 | //!
205 | //! 206 | //! # Algorithm notes 207 | //! 208 | //! The approach and terminology used in the implementation are derived from 209 | //! [*Derek C. Oppen, "Pretty Printing" (1979)*][paper], on which 210 | //! rustc_ast_pretty is also based, and from rustc_ast_pretty's implementation 211 | //! written by Graydon Hoare in 2011 (and modernized over the years by dozens of 212 | //! volunteer maintainers). 213 | //! 214 | //! [paper]: http://i.stanford.edu/pub/cstr/reports/cs/tr/79/770/CS-TR-79-770.pdf 215 | //! 216 | //! The paper describes two language-agnostic interacting procedures `Scan()` 217 | //! and `Print()`. Language-specific code decomposes an input data structure 218 | //! into a stream of `string` and `break` tokens, and `begin` and `end` tokens 219 | //! for grouping. Each `begin`–`end` range may be identified as either 220 | //! "consistent breaking" or "inconsistent breaking". If a group is consistently 221 | //! breaking, then if the whole contents do not fit on the line, *every* `break` 222 | //! token in the group will receive a linebreak. This is appropriate, for 223 | //! example, for Rust struct literals, or arguments of a function call. If a 224 | //! group is inconsistently breaking, then the `string` tokens in the group are 225 | //! greedily placed on the line until out of space, and linebroken only at those 226 | //! `break` tokens for which the next string would not fit. For example, this is 227 | //! appropriate for the contents of a braced `use` statement in Rust. 228 | //! 229 | //! Scan's job is to efficiently accumulate sizing information about groups and 230 | //! breaks. For every `begin` token we compute the distance to the matched `end` 231 | //! token, and for every `break` we compute the distance to the next `break`. 232 | //! The algorithm uses a ringbuffer to hold tokens whose size is not yet 233 | //! ascertained. The maximum size of the ringbuffer is bounded by the target 234 | //! line length and does not grow indefinitely, regardless of deep nesting in 235 | //! the input stream. That's because once a group is sufficiently big, the 236 | //! precise size can no longer make a difference to linebreak decisions and we 237 | //! can effectively treat it as "infinity". 238 | //! 239 | //! Print's job is to use the sizing information to efficiently assign a 240 | //! "broken" or "not broken" status to every `begin` token. At that point the 241 | //! output is easily constructed by concatenating `string` tokens and breaking 242 | //! at `break` tokens contained within a broken group. 243 | //! 244 | //! Leveraging these primitives (i.e. cleverly placing the all-or-nothing 245 | //! consistent breaks and greedy inconsistent breaks) to yield 246 | //! rustfmt-compatible formatting for all of Rust's syntax tree nodes is a fun 247 | //! challenge. 248 | //! 249 | //! Here is a visualization of some Rust tokens fed into the pretty printing 250 | //! algorithm. Consistently breaking `begin`—`end` pairs are represented 251 | //! by `«`⁠`»`, inconsistently breaking by `‹`⁠`›`, `break` by `·`, 252 | //! and the rest of the non-whitespace are `string`. 253 | //! 254 | //! ```text 255 | //! use crate::«{· 256 | //! ‹ lazy::«{·‹Lazy,· SyncLazy,· SyncOnceCell›·}»,· 257 | //! panic,· 258 | //! sync::«{· 259 | //! ‹ atomic::«{·‹AtomicUsize,· Ordering::SeqCst›·}»,· 260 | //! mpsc::channel,· Mutex›,· 261 | //! }»,· 262 | //! thread›,· 263 | //! }»;· 264 | //! «‹«impl<«·T‹›,· U‹›·»>» Into<«·U·»>· for T›· 265 | //! where· 266 | //! U:‹ From<«·T·»>›,· 267 | //! {· 268 | //! « fn into(·«·self·») -> U {· 269 | //! ‹ U::from(«·self·»)›· 270 | //! » }· 271 | //! »}· 272 | //! ``` 273 | //! 274 | //! The algorithm described in the paper is not quite sufficient for producing 275 | //! well-formatted Rust code that is locally indistinguishable from rustfmt's 276 | //! style. The reason is that in the paper, the complete non-whitespace contents 277 | //! are assumed to be independent of linebreak decisions, with Scan and Print 278 | //! being only in control of the whitespace (spaces and line breaks). In Rust as 279 | //! idiomatically formattted by rustfmt, that is not the case. Trailing commas 280 | //! are one example; the punctuation is only known *after* the broken vs 281 | //! non-broken status of the surrounding group is known: 282 | //! 283 | //! ``` 284 | //! # struct Struct { x: u64, y: bool } 285 | //! # let xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = 0; 286 | //! # let yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy = true; 287 | //! # 288 | //! let _ = Struct { x: 0, y: true }; 289 | //! 290 | //! let _ = Struct { 291 | //! x: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, 292 | //! y: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy, //<- trailing comma if the expression wrapped 293 | //! }; 294 | //! ``` 295 | //! 296 | //! The formatting of `match` expressions is another case; we want small arms on 297 | //! the same line as the pattern, and big arms wrapped in a brace. The presence 298 | //! of the brace punctuation, comma, and semicolon are all dependent on whether 299 | //! the arm fits on the line: 300 | //! 301 | //! ``` 302 | //! # struct Entry { nanos: u32 } 303 | //! # let total_nanos = 0u64; 304 | //! # let mut total_secs = 0u64; 305 | //! # let tmp; 306 | //! # let entry = Entry { nanos: 0 }; 307 | //! # const NANOS_PER_SEC: u32 = 1_000_000_000; 308 | //! # 309 | //! match total_nanos.checked_add(entry.nanos as u64) { 310 | //! Some(n) => tmp = n, //<- small arm, inline with comma 311 | //! None => { 312 | //! total_secs = total_secs 313 | //! .checked_add(total_nanos / NANOS_PER_SEC as u64) 314 | //! .expect("overflow in iter::sum over durations"); 315 | //! } //<- big arm, needs brace added, and also semicolon^ 316 | //! } 317 | //! ``` 318 | //! 319 | //! The printing algorithm implementation in this crate accommodates all of 320 | //! these situations with conditional punctuation tokens whose selection can be 321 | //! deferred and populated after it's known that the group is or is not broken. 322 | 323 | #![doc(html_root_url = "https://docs.rs/prettyplease/0.2.10")] 324 | #![allow( 325 | clippy::cast_possible_wrap, 326 | clippy::cast_sign_loss, 327 | clippy::derive_partial_eq_without_eq, 328 | clippy::doc_markdown, 329 | clippy::enum_glob_use, 330 | clippy::items_after_statements, 331 | clippy::let_underscore_untyped, 332 | clippy::match_like_matches_macro, 333 | clippy::match_same_arms, 334 | clippy::module_name_repetitions, 335 | clippy::must_use_candidate, 336 | clippy::needless_pass_by_value, 337 | clippy::similar_names, 338 | clippy::too_many_lines, 339 | clippy::unused_self, 340 | clippy::vec_init_then_push 341 | )] 342 | #![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))] 343 | 344 | mod algorithm; 345 | mod attr; 346 | mod convenience; 347 | mod data; 348 | mod expr; 349 | mod file; 350 | mod generics; 351 | mod item; 352 | mod iter; 353 | mod lifetime; 354 | mod lit; 355 | mod mac; 356 | mod pat; 357 | mod path; 358 | mod ring; 359 | mod stmt; 360 | mod token; 361 | mod ty; 362 | 363 | use crate::algorithm::Printer; 364 | use syn::{Expr, ExprIf, File}; 365 | 366 | // Target line width. 367 | const MARGIN: isize = 89; 368 | 369 | // Number of spaces increment at each level of block indentation. 370 | const INDENT: isize = 4; 371 | 372 | // Every line is allowed at least this much space, even if highly indented. 373 | const MIN_SPACE: isize = 60; 374 | 375 | pub fn unparse(file: &File) -> String { 376 | let mut p = Printer::new(); 377 | p.file(file); 378 | p.eof() 379 | } 380 | 381 | pub fn unparse_expr(expr: &Expr) -> String { 382 | let mut p = Printer::new(); 383 | p.expr(expr); 384 | p.eof() 385 | } 386 | 387 | pub fn unparse_expr_if(expr_if: &ExprIf) -> String { 388 | let mut p = Printer::new(); 389 | p.expr_if(expr_if); 390 | p.eof() 391 | } 392 | -------------------------------------------------------------------------------- /examples/output.rustfmt.rs: -------------------------------------------------------------------------------- 1 | use crate::cmp::Ordering; 2 | use crate::fmt::{self, Write as FmtWrite}; 3 | use crate::hash; 4 | use crate::io::Write as IoWrite; 5 | use crate::mem::transmute; 6 | use crate::sys::net::netc as c; 7 | use crate::sys_common::{AsInner, FromInner, IntoInner}; 8 | #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] 9 | pub enum IpAddr { 10 | V4(Ipv4Addr), 11 | V6(Ipv6Addr), 12 | } 13 | #[derive(Copy)] 14 | pub struct Ipv4Addr { 15 | inner: c::in_addr, 16 | } 17 | #[derive(Copy)] 18 | pub struct Ipv6Addr { 19 | inner: c::in6_addr, 20 | } 21 | #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] 22 | #[non_exhaustive] 23 | pub enum Ipv6MulticastScope { 24 | InterfaceLocal, 25 | LinkLocal, 26 | RealmLocal, 27 | AdminLocal, 28 | SiteLocal, 29 | OrganizationLocal, 30 | Global, 31 | } 32 | impl IpAddr { 33 | pub const fn is_unspecified(&self) -> bool { 34 | match self { 35 | IpAddr::V4(ip) => ip.is_unspecified(), 36 | IpAddr::V6(ip) => ip.is_unspecified(), 37 | } 38 | } 39 | pub const fn is_loopback(&self) -> bool { 40 | match self { 41 | IpAddr::V4(ip) => ip.is_loopback(), 42 | IpAddr::V6(ip) => ip.is_loopback(), 43 | } 44 | } 45 | pub const fn is_global(&self) -> bool { 46 | match self { 47 | IpAddr::V4(ip) => ip.is_global(), 48 | IpAddr::V6(ip) => ip.is_global(), 49 | } 50 | } 51 | pub const fn is_multicast(&self) -> bool { 52 | match self { 53 | IpAddr::V4(ip) => ip.is_multicast(), 54 | IpAddr::V6(ip) => ip.is_multicast(), 55 | } 56 | } 57 | pub const fn is_documentation(&self) -> bool { 58 | match self { 59 | IpAddr::V4(ip) => ip.is_documentation(), 60 | IpAddr::V6(ip) => ip.is_documentation(), 61 | } 62 | } 63 | pub const fn is_benchmarking(&self) -> bool { 64 | match self { 65 | IpAddr::V4(ip) => ip.is_benchmarking(), 66 | IpAddr::V6(ip) => ip.is_benchmarking(), 67 | } 68 | } 69 | pub const fn is_ipv4(&self) -> bool { 70 | matches!(self, IpAddr::V4(_)) 71 | } 72 | pub const fn is_ipv6(&self) -> bool { 73 | matches!(self, IpAddr::V6(_)) 74 | } 75 | pub const fn to_canonical(&self) -> IpAddr { 76 | match self { 77 | &v4 @ IpAddr::V4(_) => v4, 78 | IpAddr::V6(v6) => v6.to_canonical(), 79 | } 80 | } 81 | } 82 | impl Ipv4Addr { 83 | pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { 84 | Ipv4Addr { 85 | inner: c::in_addr { 86 | s_addr: u32::from_ne_bytes([a, b, c, d]), 87 | }, 88 | } 89 | } 90 | pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); 91 | #[doc(alias = "INADDR_ANY")] 92 | pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); 93 | pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); 94 | pub const fn octets(&self) -> [u8; 4] { 95 | self.inner.s_addr.to_ne_bytes() 96 | } 97 | pub const fn is_unspecified(&self) -> bool { 98 | self.inner.s_addr == 0 99 | } 100 | pub const fn is_loopback(&self) -> bool { 101 | self.octets()[0] == 127 102 | } 103 | pub const fn is_private(&self) -> bool { 104 | match self.octets() { 105 | [10, ..] => true, 106 | [172, b, ..] if b >= 16 && b <= 31 => true, 107 | [192, 168, ..] => true, 108 | _ => false, 109 | } 110 | } 111 | pub const fn is_link_local(&self) -> bool { 112 | matches!(self.octets(), [169, 254, ..]) 113 | } 114 | pub const fn is_global(&self) -> bool { 115 | if u32::from_be_bytes(self.octets()) == 0xc0000009 116 | || u32::from_be_bytes(self.octets()) == 0xc000000a 117 | { 118 | return true; 119 | } 120 | !self.is_private() 121 | && !self.is_loopback() 122 | && !self.is_link_local() 123 | && !self.is_broadcast() 124 | && !self.is_documentation() 125 | && !self.is_shared() 126 | && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) 127 | && !self.is_reserved() 128 | && !self.is_benchmarking() 129 | && self.octets()[0] != 0 130 | } 131 | pub const fn is_shared(&self) -> bool { 132 | self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) 133 | } 134 | pub const fn is_benchmarking(&self) -> bool { 135 | self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 136 | } 137 | pub const fn is_reserved(&self) -> bool { 138 | self.octets()[0] & 240 == 240 && !self.is_broadcast() 139 | } 140 | pub const fn is_multicast(&self) -> bool { 141 | self.octets()[0] >= 224 && self.octets()[0] <= 239 142 | } 143 | pub const fn is_broadcast(&self) -> bool { 144 | u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) 145 | } 146 | pub const fn is_documentation(&self) -> bool { 147 | matches!( 148 | self.octets(), 149 | [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _] 150 | ) 151 | } 152 | pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { 153 | let [a, b, c, d] = self.octets(); 154 | Ipv6Addr { 155 | inner: c::in6_addr { 156 | s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d], 157 | }, 158 | } 159 | } 160 | pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { 161 | let [a, b, c, d] = self.octets(); 162 | Ipv6Addr { 163 | inner: c::in6_addr { 164 | s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d], 165 | }, 166 | } 167 | } 168 | } 169 | impl fmt::Display for IpAddr { 170 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 171 | match self { 172 | IpAddr::V4(ip) => ip.fmt(fmt), 173 | IpAddr::V6(ip) => ip.fmt(fmt), 174 | } 175 | } 176 | } 177 | impl fmt::Debug for IpAddr { 178 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 179 | fmt::Display::fmt(self, fmt) 180 | } 181 | } 182 | impl From for IpAddr { 183 | fn from(ipv4: Ipv4Addr) -> IpAddr { 184 | IpAddr::V4(ipv4) 185 | } 186 | } 187 | impl From for IpAddr { 188 | fn from(ipv6: Ipv6Addr) -> IpAddr { 189 | IpAddr::V6(ipv6) 190 | } 191 | } 192 | impl fmt::Display for Ipv4Addr { 193 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 194 | let octets = self.octets(); 195 | if fmt.precision().is_none() && fmt.width().is_none() { 196 | write!( 197 | fmt, 198 | "{}.{}.{}.{}", 199 | octets[0], octets[1], octets[2], octets[3] 200 | ) 201 | } else { 202 | const IPV4_BUF_LEN: usize = 15; 203 | let mut buf = [0u8; IPV4_BUF_LEN]; 204 | let mut buf_slice = &mut buf[..]; 205 | write!( 206 | buf_slice, 207 | "{}.{}.{}.{}", 208 | octets[0], octets[1], octets[2], octets[3] 209 | ) 210 | .unwrap(); 211 | let len = IPV4_BUF_LEN - buf_slice.len(); 212 | let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; 213 | fmt.pad(buf) 214 | } 215 | } 216 | } 217 | impl fmt::Debug for Ipv4Addr { 218 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 219 | fmt::Display::fmt(self, fmt) 220 | } 221 | } 222 | impl Clone for Ipv4Addr { 223 | fn clone(&self) -> Ipv4Addr { 224 | *self 225 | } 226 | } 227 | impl PartialEq for Ipv4Addr { 228 | fn eq(&self, other: &Ipv4Addr) -> bool { 229 | self.inner.s_addr == other.inner.s_addr 230 | } 231 | } 232 | impl PartialEq for IpAddr { 233 | fn eq(&self, other: &Ipv4Addr) -> bool { 234 | match self { 235 | IpAddr::V4(v4) => v4 == other, 236 | IpAddr::V6(_) => false, 237 | } 238 | } 239 | } 240 | impl PartialEq for Ipv4Addr { 241 | fn eq(&self, other: &IpAddr) -> bool { 242 | match other { 243 | IpAddr::V4(v4) => self == v4, 244 | IpAddr::V6(_) => false, 245 | } 246 | } 247 | } 248 | impl Eq for Ipv4Addr {} 249 | impl hash::Hash for Ipv4Addr { 250 | fn hash(&self, s: &mut H) { 251 | { self.inner.s_addr }.hash(s) 252 | } 253 | } 254 | impl PartialOrd for Ipv4Addr { 255 | fn partial_cmp(&self, other: &Ipv4Addr) -> Option { 256 | Some(self.cmp(other)) 257 | } 258 | } 259 | impl PartialOrd for IpAddr { 260 | fn partial_cmp(&self, other: &Ipv4Addr) -> Option { 261 | match self { 262 | IpAddr::V4(v4) => v4.partial_cmp(other), 263 | IpAddr::V6(_) => Some(Ordering::Greater), 264 | } 265 | } 266 | } 267 | impl PartialOrd for Ipv4Addr { 268 | fn partial_cmp(&self, other: &IpAddr) -> Option { 269 | match other { 270 | IpAddr::V4(v4) => self.partial_cmp(v4), 271 | IpAddr::V6(_) => Some(Ordering::Less), 272 | } 273 | } 274 | } 275 | impl Ord for Ipv4Addr { 276 | fn cmp(&self, other: &Ipv4Addr) -> Ordering { 277 | u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) 278 | } 279 | } 280 | impl IntoInner for Ipv4Addr { 281 | fn into_inner(self) -> c::in_addr { 282 | self.inner 283 | } 284 | } 285 | impl From for u32 { 286 | fn from(ip: Ipv4Addr) -> u32 { 287 | let ip = ip.octets(); 288 | u32::from_be_bytes(ip) 289 | } 290 | } 291 | impl From for Ipv4Addr { 292 | fn from(ip: u32) -> Ipv4Addr { 293 | Ipv4Addr::from(ip.to_be_bytes()) 294 | } 295 | } 296 | impl From<[u8; 4]> for Ipv4Addr { 297 | fn from(octets: [u8; 4]) -> Ipv4Addr { 298 | Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) 299 | } 300 | } 301 | impl From<[u8; 4]> for IpAddr { 302 | fn from(octets: [u8; 4]) -> IpAddr { 303 | IpAddr::V4(Ipv4Addr::from(octets)) 304 | } 305 | } 306 | impl Ipv6Addr { 307 | pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { 308 | let addr16 = [ 309 | a.to_be(), 310 | b.to_be(), 311 | c.to_be(), 312 | d.to_be(), 313 | e.to_be(), 314 | f.to_be(), 315 | g.to_be(), 316 | h.to_be(), 317 | ]; 318 | Ipv6Addr { 319 | inner: c::in6_addr { 320 | s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, 321 | }, 322 | } 323 | } 324 | pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); 325 | pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); 326 | pub const fn segments(&self) -> [u16; 8] { 327 | let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; 328 | [ 329 | u16::from_be(a), 330 | u16::from_be(b), 331 | u16::from_be(c), 332 | u16::from_be(d), 333 | u16::from_be(e), 334 | u16::from_be(f), 335 | u16::from_be(g), 336 | u16::from_be(h), 337 | ] 338 | } 339 | pub const fn is_unspecified(&self) -> bool { 340 | u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) 341 | } 342 | pub const fn is_loopback(&self) -> bool { 343 | u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) 344 | } 345 | pub const fn is_global(&self) -> bool { 346 | match self.multicast_scope() { 347 | Some(Ipv6MulticastScope::Global) => true, 348 | None => self.is_unicast_global(), 349 | _ => false, 350 | } 351 | } 352 | pub const fn is_unique_local(&self) -> bool { 353 | (self.segments()[0] & 0xfe00) == 0xfc00 354 | } 355 | pub const fn is_unicast(&self) -> bool { 356 | !self.is_multicast() 357 | } 358 | pub const fn is_unicast_link_local(&self) -> bool { 359 | (self.segments()[0] & 0xffc0) == 0xfe80 360 | } 361 | pub const fn is_documentation(&self) -> bool { 362 | (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) 363 | } 364 | pub const fn is_benchmarking(&self) -> bool { 365 | (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) 366 | } 367 | pub const fn is_unicast_global(&self) -> bool { 368 | self.is_unicast() 369 | && !self.is_loopback() 370 | && !self.is_unicast_link_local() 371 | && !self.is_unique_local() 372 | && !self.is_unspecified() 373 | && !self.is_documentation() 374 | } 375 | pub const fn multicast_scope(&self) -> Option { 376 | if self.is_multicast() { 377 | match self.segments()[0] & 0x000f { 378 | 1 => Some(Ipv6MulticastScope::InterfaceLocal), 379 | 2 => Some(Ipv6MulticastScope::LinkLocal), 380 | 3 => Some(Ipv6MulticastScope::RealmLocal), 381 | 4 => Some(Ipv6MulticastScope::AdminLocal), 382 | 5 => Some(Ipv6MulticastScope::SiteLocal), 383 | 8 => Some(Ipv6MulticastScope::OrganizationLocal), 384 | 14 => Some(Ipv6MulticastScope::Global), 385 | _ => None, 386 | } 387 | } else { 388 | None 389 | } 390 | } 391 | pub const fn is_multicast(&self) -> bool { 392 | (self.segments()[0] & 0xff00) == 0xff00 393 | } 394 | pub const fn to_ipv4_mapped(&self) -> Option { 395 | match self.octets() { 396 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { 397 | Some(Ipv4Addr::new(a, b, c, d)) 398 | } 399 | _ => None, 400 | } 401 | } 402 | pub const fn to_ipv4(&self) -> Option { 403 | if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { 404 | let [a, b] = ab.to_be_bytes(); 405 | let [c, d] = cd.to_be_bytes(); 406 | Some(Ipv4Addr::new(a, b, c, d)) 407 | } else { 408 | None 409 | } 410 | } 411 | pub const fn to_canonical(&self) -> IpAddr { 412 | if let Some(mapped) = self.to_ipv4_mapped() { 413 | return IpAddr::V4(mapped); 414 | } 415 | IpAddr::V6(*self) 416 | } 417 | pub const fn octets(&self) -> [u8; 16] { 418 | self.inner.s6_addr 419 | } 420 | } 421 | impl fmt::Display for Ipv6Addr { 422 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 423 | if f.precision().is_none() && f.width().is_none() { 424 | let segments = self.segments(); 425 | if self.is_unspecified() { 426 | f.write_str("::") 427 | } else if self.is_loopback() { 428 | f.write_str("::1") 429 | } else if let Some(ipv4) = self.to_ipv4() { 430 | match segments[5] { 431 | 0 => write!(f, "::{}", ipv4), 432 | 0xffff => write!(f, "::ffff:{}", ipv4), 433 | _ => unreachable!(), 434 | } 435 | } else { # [derive (Copy , Clone , Default)] struct Span { start : usize , len : usize , } let zeroes = { let mut longest = Span :: default () ; let mut current = Span :: default () ; for (i , & segment) in segments . iter () . enumerate () { if segment == 0 { if current . len == 0 { current . start = i ; } current . len += 1 ; if current . len > longest . len { longest = current ; } } else { current = Span :: default () ; } } longest } ; # [doc = " Write a colon-separated part of the address"] # [inline] fn fmt_subslice (f : & mut fmt :: Formatter < '_ > , chunk : & [u16]) -> fmt :: Result { if let Some ((first , tail)) = chunk . split_first () { write ! (f , "{:x}" , first) ? ; for segment in tail { f . write_char (':') ? ; write ! (f , "{:x}" , segment) ? ; } } Ok (()) } if zeroes . len > 1 { fmt_subslice (f , & segments [.. zeroes . start]) ? ; f . write_str ("::") ? ; fmt_subslice (f , & segments [zeroes . start + zeroes . len ..]) } else { fmt_subslice (f , & segments) } } 436 | } else { 437 | const IPV6_BUF_LEN: usize = (4 * 8) + 7; 438 | let mut buf = [0u8; IPV6_BUF_LEN]; 439 | let mut buf_slice = &mut buf[..]; 440 | write!(buf_slice, "{}", self).unwrap(); 441 | let len = IPV6_BUF_LEN - buf_slice.len(); 442 | let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; 443 | f.pad(buf) 444 | } 445 | } 446 | } 447 | impl fmt::Debug for Ipv6Addr { 448 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 449 | fmt::Display::fmt(self, fmt) 450 | } 451 | } 452 | impl Clone for Ipv6Addr { 453 | fn clone(&self) -> Ipv6Addr { 454 | *self 455 | } 456 | } 457 | impl PartialEq for Ipv6Addr { 458 | fn eq(&self, other: &Ipv6Addr) -> bool { 459 | self.inner.s6_addr == other.inner.s6_addr 460 | } 461 | } 462 | impl PartialEq for Ipv6Addr { 463 | fn eq(&self, other: &IpAddr) -> bool { 464 | match other { 465 | IpAddr::V4(_) => false, 466 | IpAddr::V6(v6) => self == v6, 467 | } 468 | } 469 | } 470 | impl PartialEq for IpAddr { 471 | fn eq(&self, other: &Ipv6Addr) -> bool { 472 | match self { 473 | IpAddr::V4(_) => false, 474 | IpAddr::V6(v6) => v6 == other, 475 | } 476 | } 477 | } 478 | impl Eq for Ipv6Addr {} 479 | impl hash::Hash for Ipv6Addr { 480 | fn hash(&self, s: &mut H) { 481 | self.inner.s6_addr.hash(s) 482 | } 483 | } 484 | impl PartialOrd for Ipv6Addr { 485 | fn partial_cmp(&self, other: &Ipv6Addr) -> Option { 486 | Some(self.cmp(other)) 487 | } 488 | } 489 | impl PartialOrd for IpAddr { 490 | fn partial_cmp(&self, other: &Ipv6Addr) -> Option { 491 | match self { 492 | IpAddr::V4(_) => Some(Ordering::Less), 493 | IpAddr::V6(v6) => v6.partial_cmp(other), 494 | } 495 | } 496 | } 497 | impl PartialOrd for Ipv6Addr { 498 | fn partial_cmp(&self, other: &IpAddr) -> Option { 499 | match other { 500 | IpAddr::V4(_) => Some(Ordering::Greater), 501 | IpAddr::V6(v6) => self.partial_cmp(v6), 502 | } 503 | } 504 | } 505 | impl Ord for Ipv6Addr { 506 | fn cmp(&self, other: &Ipv6Addr) -> Ordering { 507 | self.segments().cmp(&other.segments()) 508 | } 509 | } 510 | impl AsInner for Ipv6Addr { 511 | fn as_inner(&self) -> &c::in6_addr { 512 | &self.inner 513 | } 514 | } 515 | impl FromInner for Ipv6Addr { 516 | fn from_inner(addr: c::in6_addr) -> Ipv6Addr { 517 | Ipv6Addr { inner: addr } 518 | } 519 | } 520 | impl From for u128 { 521 | fn from(ip: Ipv6Addr) -> u128 { 522 | let ip = ip.octets(); 523 | u128::from_be_bytes(ip) 524 | } 525 | } 526 | impl From for Ipv6Addr { 527 | fn from(ip: u128) -> Ipv6Addr { 528 | Ipv6Addr::from(ip.to_be_bytes()) 529 | } 530 | } 531 | impl From<[u8; 16]> for Ipv6Addr { 532 | fn from(octets: [u8; 16]) -> Ipv6Addr { 533 | let inner = c::in6_addr { s6_addr: octets }; 534 | Ipv6Addr::from_inner(inner) 535 | } 536 | } 537 | impl From<[u16; 8]> for Ipv6Addr { 538 | fn from(segments: [u16; 8]) -> Ipv6Addr { 539 | let [a, b, c, d, e, f, g, h] = segments; 540 | Ipv6Addr::new(a, b, c, d, e, f, g, h) 541 | } 542 | } 543 | impl From<[u8; 16]> for IpAddr { 544 | fn from(octets: [u8; 16]) -> IpAddr { 545 | IpAddr::V6(Ipv6Addr::from(octets)) 546 | } 547 | } 548 | impl From<[u16; 8]> for IpAddr { 549 | fn from(segments: [u16; 8]) -> IpAddr { 550 | IpAddr::V6(Ipv6Addr::from(segments)) 551 | } 552 | } 553 | -------------------------------------------------------------------------------- /examples/output.rustc.rs: -------------------------------------------------------------------------------- 1 | use crate::cmp::Ordering;use crate::fmt::{self, Write as FmtWrite}; 2 | use crate::hash; 3 | use crate::io::Write as IoWrite; 4 | use crate::mem::transmute; 5 | use crate::sys::net::netc as c; 6 | use crate::sys_common::{AsInner, FromInner, IntoInner}; 7 | #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] 8 | pub enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), } 9 | #[derive(Copy)] 10 | pub struct Ipv4Addr { 11 | inner: c::in_addr, 12 | } 13 | #[derive(Copy)] 14 | pub struct Ipv6Addr { 15 | inner: c::in6_addr, 16 | } 17 | #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] 18 | #[non_exhaustive] 19 | pub enum Ipv6MulticastScope { 20 | InterfaceLocal, 21 | LinkLocal, 22 | RealmLocal, 23 | AdminLocal, 24 | SiteLocal, 25 | OrganizationLocal, 26 | Global, 27 | } 28 | impl IpAddr { 29 | pub const fn is_unspecified(&self) -> bool { 30 | match self { 31 | IpAddr::V4(ip) => ip.is_unspecified(), 32 | IpAddr::V6(ip) => ip.is_unspecified(), 33 | } 34 | } 35 | pub const fn is_loopback(&self) -> bool { 36 | match self { 37 | IpAddr::V4(ip) => ip.is_loopback(), 38 | IpAddr::V6(ip) => ip.is_loopback(), 39 | } 40 | } 41 | pub const fn is_global(&self) -> bool { 42 | match self { 43 | IpAddr::V4(ip) => ip.is_global(), 44 | IpAddr::V6(ip) => ip.is_global(), 45 | } 46 | } 47 | pub const fn is_multicast(&self) -> bool { 48 | match self { 49 | IpAddr::V4(ip) => ip.is_multicast(), 50 | IpAddr::V6(ip) => ip.is_multicast(), 51 | } 52 | } 53 | pub const fn is_documentation(&self) -> bool { 54 | match self { 55 | IpAddr::V4(ip) => ip.is_documentation(), 56 | IpAddr::V6(ip) => ip.is_documentation(), 57 | } 58 | } 59 | pub const fn is_benchmarking(&self) -> bool { 60 | match self { 61 | IpAddr::V4(ip) => ip.is_benchmarking(), 62 | IpAddr::V6(ip) => ip.is_benchmarking(), 63 | } 64 | } 65 | pub const fn is_ipv4(&self) -> bool { matches!(self, IpAddr :: V4(_)) } 66 | pub const fn is_ipv6(&self) -> bool { matches!(self, IpAddr :: V6(_)) } 67 | pub const fn to_canonical(&self) -> IpAddr { 68 | match self { 69 | &v4 @ IpAddr::V4(_) => v4, 70 | IpAddr::V6(v6) => v6.to_canonical(), 71 | } 72 | } 73 | } 74 | impl Ipv4Addr { 75 | pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { 76 | Ipv4Addr { 77 | inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]) }, 78 | } 79 | } 80 | pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); 81 | #[doc(alias = "INADDR_ANY")] 82 | pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); 83 | pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); 84 | pub const fn octets(&self) -> [u8; 4] { self.inner.s_addr.to_ne_bytes() } 85 | pub const fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } 86 | pub const fn is_loopback(&self) -> bool { self.octets()[0] == 127 } 87 | pub const fn is_private(&self) -> bool { 88 | match self.octets() { 89 | [10, ..] => true, 90 | [172, b, ..] if b >= 16 && b <= 31 => true, 91 | [192, 168, ..] => true, 92 | _ => false, 93 | } 94 | } 95 | pub const fn is_link_local(&self) -> bool { 96 | matches!(self.octets(), [169, 254, ..]) 97 | } 98 | pub const fn is_global(&self) -> bool { 99 | if u32::from_be_bytes(self.octets()) == 0xc0000009 || 100 | u32::from_be_bytes(self.octets()) == 0xc000000a { 101 | return true; 102 | } 103 | !self.is_private() && !self.is_loopback() && !self.is_link_local() && 104 | !self.is_broadcast() && !self.is_documentation() && 105 | !self.is_shared() && 106 | !(self.octets()[0] == 192 && self.octets()[1] == 0 && 107 | self.octets()[2] == 0) && !self.is_reserved() && 108 | !self.is_benchmarking() && self.octets()[0] != 0 109 | } 110 | pub const fn is_shared(&self) -> bool { 111 | self.octets()[0] == 100 && 112 | (self.octets()[1] & 0b1100_0000 == 0b0100_0000) 113 | } 114 | pub const fn is_benchmarking(&self) -> bool { 115 | self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 116 | } 117 | pub const fn is_reserved(&self) -> bool { 118 | self.octets()[0] & 240 == 240 && !self.is_broadcast() 119 | } 120 | pub const fn is_multicast(&self) -> bool { 121 | self.octets()[0] >= 224 && self.octets()[0] <= 239 122 | } 123 | pub const fn is_broadcast(&self) -> bool { 124 | u32::from_be_bytes(self.octets()) == 125 | u32::from_be_bytes(Self::BROADCAST.octets()) 126 | } 127 | pub const fn is_documentation(&self) -> bool { 128 | matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | 129 | [203, 0, 113, _]) 130 | } 131 | pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { 132 | let [a, b, c, d] = self.octets(); 133 | Ipv6Addr { 134 | inner: c::in6_addr { 135 | s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d], 136 | }, 137 | } 138 | } 139 | pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { 140 | let [a, b, c, d] = self.octets(); 141 | Ipv6Addr { 142 | inner: c::in6_addr { 143 | s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, 144 | d], 145 | }, 146 | } 147 | } 148 | } 149 | impl fmt::Display for IpAddr { 150 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 151 | match self { 152 | IpAddr::V4(ip) => ip.fmt(fmt), 153 | IpAddr::V6(ip) => ip.fmt(fmt), 154 | } 155 | } 156 | } 157 | impl fmt::Debug for IpAddr { 158 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 159 | fmt::Display::fmt(self, fmt) 160 | } 161 | } 162 | impl From for IpAddr { 163 | fn from(ipv4: Ipv4Addr) -> IpAddr { IpAddr::V4(ipv4) } 164 | } 165 | impl From for IpAddr { 166 | fn from(ipv6: Ipv6Addr) -> IpAddr { IpAddr::V6(ipv6) } 167 | } 168 | impl fmt::Display for Ipv4Addr { 169 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 170 | let octets = self.octets(); 171 | if fmt.precision().is_none() && fmt.width().is_none() { 172 | write!(fmt, "{}.{}.{}.{}", octets [0], octets [1], octets [2], 173 | octets [3]) 174 | } else { 175 | const IPV4_BUF_LEN: usize = 15; 176 | let mut buf = [0u8; IPV4_BUF_LEN]; 177 | let mut buf_slice = &mut buf[..]; 178 | write!(buf_slice, "{}.{}.{}.{}", octets [0], octets [1], octets 179 | [2], octets [3]).unwrap(); 180 | let len = IPV4_BUF_LEN - buf_slice.len(); 181 | let buf = 182 | unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; 183 | fmt.pad(buf) 184 | } 185 | } 186 | } 187 | impl fmt::Debug for Ipv4Addr { 188 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 189 | fmt::Display::fmt(self, fmt) 190 | } 191 | } 192 | impl Clone for Ipv4Addr { 193 | fn clone(&self) -> Ipv4Addr { *self } 194 | } 195 | impl PartialEq for Ipv4Addr { 196 | fn eq(&self, other: &Ipv4Addr) -> bool { 197 | self.inner.s_addr == other.inner.s_addr 198 | } 199 | } 200 | impl PartialEq for IpAddr { 201 | fn eq(&self, other: &Ipv4Addr) -> bool { 202 | match self { IpAddr::V4(v4) => v4 == other, IpAddr::V6(_) => false, } 203 | } 204 | } 205 | impl PartialEq for Ipv4Addr { 206 | fn eq(&self, other: &IpAddr) -> bool { 207 | match other { IpAddr::V4(v4) => self == v4, IpAddr::V6(_) => false, } 208 | } 209 | } 210 | impl Eq for Ipv4Addr {} 211 | impl hash::Hash for Ipv4Addr { 212 | fn hash(&self, s: &mut H) { 213 | { self.inner.s_addr }.hash(s) 214 | } 215 | } 216 | impl PartialOrd for Ipv4Addr { 217 | fn partial_cmp(&self, other: &Ipv4Addr) -> Option { 218 | Some(self.cmp(other)) 219 | } 220 | } 221 | impl PartialOrd for IpAddr { 222 | fn partial_cmp(&self, other: &Ipv4Addr) -> Option { 223 | match self { 224 | IpAddr::V4(v4) => v4.partial_cmp(other), 225 | IpAddr::V6(_) => Some(Ordering::Greater), 226 | } 227 | } 228 | } 229 | impl PartialOrd for Ipv4Addr { 230 | fn partial_cmp(&self, other: &IpAddr) -> Option { 231 | match other { 232 | IpAddr::V4(v4) => self.partial_cmp(v4), 233 | IpAddr::V6(_) => Some(Ordering::Less), 234 | } 235 | } 236 | } 237 | impl Ord for Ipv4Addr { 238 | fn cmp(&self, other: &Ipv4Addr) -> Ordering { 239 | u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) 240 | } 241 | } 242 | impl IntoInner for Ipv4Addr { 243 | fn into_inner(self) -> c::in_addr { self.inner } 244 | } 245 | impl From for u32 { 246 | fn from(ip: Ipv4Addr) -> u32 { 247 | let ip = ip.octets(); 248 | u32::from_be_bytes(ip) 249 | } 250 | } 251 | impl From for Ipv4Addr { 252 | fn from(ip: u32) -> Ipv4Addr { Ipv4Addr::from(ip.to_be_bytes()) } 253 | } 254 | impl From<[u8; 4]> for Ipv4Addr { 255 | fn from(octets: [u8; 4]) -> Ipv4Addr { 256 | Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) 257 | } 258 | } 259 | impl From<[u8; 4]> for IpAddr { 260 | fn from(octets: [u8; 4]) -> IpAddr { IpAddr::V4(Ipv4Addr::from(octets)) } 261 | } 262 | impl Ipv6Addr { 263 | pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, 264 | h: u16) -> Ipv6Addr { 265 | let addr16 = 266 | [a.to_be(), b.to_be(), c.to_be(), d.to_be(), e.to_be(), f.to_be(), 267 | g.to_be(), h.to_be()]; 268 | Ipv6Addr { 269 | inner: c::in6_addr { 270 | s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, 271 | }, 272 | } 273 | } 274 | pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); 275 | pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); 276 | pub const fn segments(&self) -> [u16; 8] { 277 | let [a, b, c, d, e, f, g, h] = 278 | unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; 279 | [u16::from_be(a), u16::from_be(b), u16::from_be(c), u16::from_be(d), 280 | u16::from_be(e), u16::from_be(f), u16::from_be(g), 281 | u16::from_be(h)] 282 | } 283 | pub const fn is_unspecified(&self) -> bool { 284 | u128::from_be_bytes(self.octets()) == 285 | u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) 286 | } 287 | pub const fn is_loopback(&self) -> bool { 288 | u128::from_be_bytes(self.octets()) == 289 | u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) 290 | } 291 | pub const fn is_global(&self) -> bool { 292 | match self.multicast_scope() { 293 | Some(Ipv6MulticastScope::Global) => true, 294 | None => self.is_unicast_global(), 295 | _ => false, 296 | } 297 | } 298 | pub const fn is_unique_local(&self) -> bool { 299 | (self.segments()[0] & 0xfe00) == 0xfc00 300 | } 301 | pub const fn is_unicast(&self) -> bool { !self.is_multicast() } 302 | pub const fn is_unicast_link_local(&self) -> bool { 303 | (self.segments()[0] & 0xffc0) == 0xfe80 304 | } 305 | pub const fn is_documentation(&self) -> bool { 306 | (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) 307 | } 308 | pub const fn is_benchmarking(&self) -> bool { 309 | (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && 310 | (self.segments()[2] == 0) 311 | } 312 | pub const fn is_unicast_global(&self) -> bool { 313 | self.is_unicast() && !self.is_loopback() && 314 | !self.is_unicast_link_local() && !self.is_unique_local() && 315 | !self.is_unspecified() && !self.is_documentation() 316 | } 317 | pub const fn multicast_scope(&self) -> Option { 318 | if self.is_multicast() { 319 | match self.segments()[0] & 0x000f { 320 | 1 => Some(Ipv6MulticastScope::InterfaceLocal), 321 | 2 => Some(Ipv6MulticastScope::LinkLocal), 322 | 3 => Some(Ipv6MulticastScope::RealmLocal), 323 | 4 => Some(Ipv6MulticastScope::AdminLocal), 324 | 5 => Some(Ipv6MulticastScope::SiteLocal), 325 | 8 => Some(Ipv6MulticastScope::OrganizationLocal), 326 | 14 => Some(Ipv6MulticastScope::Global), 327 | _ => None, 328 | } 329 | } else { None } 330 | } 331 | pub const fn is_multicast(&self) -> bool { 332 | (self.segments()[0] & 0xff00) == 0xff00 333 | } 334 | pub const fn to_ipv4_mapped(&self) -> Option { 335 | match self.octets() { 336 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { 337 | Some(Ipv4Addr::new(a, b, c, d)) 338 | } 339 | _ => None, 340 | } 341 | } 342 | pub const fn to_ipv4(&self) -> Option { 343 | if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { 344 | let [a, b] = ab.to_be_bytes(); 345 | let [c, d] = cd.to_be_bytes(); 346 | Some(Ipv4Addr::new(a, b, c, d)) 347 | } else { None } 348 | } 349 | pub const fn to_canonical(&self) -> IpAddr { 350 | if let Some(mapped) = self.to_ipv4_mapped() { 351 | return IpAddr::V4(mapped); 352 | } 353 | IpAddr::V6(*self) 354 | } 355 | pub const fn octets(&self) -> [u8; 16] { self.inner.s6_addr } 356 | } 357 | impl fmt::Display for Ipv6Addr { 358 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 359 | if f.precision().is_none() && f.width().is_none() { 360 | let segments = self.segments(); 361 | if self.is_unspecified() { 362 | f.write_str("::") 363 | } else if self.is_loopback() { 364 | f.write_str("::1") 365 | } else if let Some(ipv4) = self.to_ipv4() { 366 | match segments[5] { 367 | 0 => write!(f, "::{}", ipv4), 368 | 0xffff => write!(f, "::ffff:{}", ipv4), 369 | _ => unreachable!(), 370 | } 371 | } else { 372 | #[derive(Copy, Clone, Default)] 373 | struct Span { 374 | start: usize, 375 | len: usize, 376 | } 377 | let zeroes = 378 | { 379 | let mut longest = Span::default(); 380 | let mut current = Span::default(); 381 | for (i, &segment) in segments.iter().enumerate() { 382 | if segment == 0 { 383 | if current.len == 0 { current.start = i; } 384 | current.len += 1; 385 | if current.len > longest.len { longest = current; } 386 | } else { current = Span::default(); } 387 | } 388 | longest 389 | }; 390 | #[doc = " Write a colon-separated part of the address"] 391 | #[inline] 392 | fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) 393 | -> fmt::Result { 394 | if let Some((first, tail)) = chunk.split_first() { 395 | write!(f, "{:x}", first)?; 396 | for segment in tail { 397 | f.write_char(':')?; 398 | write!(f, "{:x}", segment)?; 399 | } 400 | } 401 | Ok(()) 402 | } 403 | if zeroes.len > 1 { 404 | fmt_subslice(f, &segments[..zeroes.start])?; 405 | f.write_str("::")?; 406 | fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) 407 | } else { fmt_subslice(f, &segments) } 408 | } 409 | } else { 410 | const IPV6_BUF_LEN: usize = (4 * 8) + 7; 411 | let mut buf = [0u8; IPV6_BUF_LEN]; 412 | let mut buf_slice = &mut buf[..]; 413 | write!(buf_slice, "{}", self).unwrap(); 414 | let len = IPV6_BUF_LEN - buf_slice.len(); 415 | let buf = 416 | unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; 417 | f.pad(buf) 418 | } 419 | } 420 | } 421 | impl fmt::Debug for Ipv6Addr { 422 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 423 | fmt::Display::fmt(self, fmt) 424 | } 425 | } 426 | impl Clone for Ipv6Addr { 427 | fn clone(&self) -> Ipv6Addr { *self } 428 | } 429 | impl PartialEq for Ipv6Addr { 430 | fn eq(&self, other: &Ipv6Addr) -> bool { 431 | self.inner.s6_addr == other.inner.s6_addr 432 | } 433 | } 434 | impl PartialEq for Ipv6Addr { 435 | fn eq(&self, other: &IpAddr) -> bool { 436 | match other { IpAddr::V4(_) => false, IpAddr::V6(v6) => self == v6, } 437 | } 438 | } 439 | impl PartialEq for IpAddr { 440 | fn eq(&self, other: &Ipv6Addr) -> bool { 441 | match self { IpAddr::V4(_) => false, IpAddr::V6(v6) => v6 == other, } 442 | } 443 | } 444 | impl Eq for Ipv6Addr {} 445 | impl hash::Hash for Ipv6Addr { 446 | fn hash(&self, s: &mut H) { self.inner.s6_addr.hash(s) } 447 | } 448 | impl PartialOrd for Ipv6Addr { 449 | fn partial_cmp(&self, other: &Ipv6Addr) -> Option { 450 | Some(self.cmp(other)) 451 | } 452 | } 453 | impl PartialOrd for IpAddr { 454 | fn partial_cmp(&self, other: &Ipv6Addr) -> Option { 455 | match self { 456 | IpAddr::V4(_) => Some(Ordering::Less), 457 | IpAddr::V6(v6) => v6.partial_cmp(other), 458 | } 459 | } 460 | } 461 | impl PartialOrd for Ipv6Addr { 462 | fn partial_cmp(&self, other: &IpAddr) -> Option { 463 | match other { 464 | IpAddr::V4(_) => Some(Ordering::Greater), 465 | IpAddr::V6(v6) => self.partial_cmp(v6), 466 | } 467 | } 468 | } 469 | impl Ord for Ipv6Addr { 470 | fn cmp(&self, other: &Ipv6Addr) -> Ordering { 471 | self.segments().cmp(&other.segments()) 472 | } 473 | } 474 | impl AsInner for Ipv6Addr { 475 | fn as_inner(&self) -> &c::in6_addr { &self.inner } 476 | } 477 | impl FromInner for Ipv6Addr { 478 | fn from_inner(addr: c::in6_addr) -> Ipv6Addr { Ipv6Addr { inner: addr } } 479 | } 480 | impl From for u128 { 481 | fn from(ip: Ipv6Addr) -> u128 { 482 | let ip = ip.octets(); 483 | u128::from_be_bytes(ip) 484 | } 485 | } 486 | impl From for Ipv6Addr { 487 | fn from(ip: u128) -> Ipv6Addr { Ipv6Addr::from(ip.to_be_bytes()) } 488 | } 489 | impl From<[u8; 16]> for Ipv6Addr { 490 | fn from(octets: [u8; 16]) -> Ipv6Addr { 491 | let inner = c::in6_addr { s6_addr: octets }; 492 | Ipv6Addr::from_inner(inner) 493 | } 494 | } 495 | impl From<[u16; 8]> for Ipv6Addr { 496 | fn from(segments: [u16; 8]) -> Ipv6Addr { 497 | let [a, b, c, d, e, f, g, h] = segments; 498 | Ipv6Addr::new(a, b, c, d, e, f, g, h) 499 | } 500 | } 501 | impl From<[u8; 16]> for IpAddr { 502 | fn from(octets: [u8; 16]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(octets)) } 503 | } 504 | impl From<[u16; 8]> for IpAddr { 505 | fn from(segments: [u16; 8]) -> IpAddr { 506 | IpAddr::V6(Ipv6Addr::from(segments)) 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /examples/output.prettyplease.rs: -------------------------------------------------------------------------------- 1 | use crate::cmp::Ordering; 2 | use crate::fmt::{self, Write as FmtWrite}; 3 | use crate::hash; 4 | use crate::io::Write as IoWrite; 5 | use crate::mem::transmute; 6 | use crate::sys::net::netc as c; 7 | use crate::sys_common::{AsInner, FromInner, IntoInner}; 8 | #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] 9 | pub enum IpAddr { 10 | V4(Ipv4Addr), 11 | V6(Ipv6Addr), 12 | } 13 | #[derive(Copy)] 14 | pub struct Ipv4Addr { 15 | inner: c::in_addr, 16 | } 17 | #[derive(Copy)] 18 | pub struct Ipv6Addr { 19 | inner: c::in6_addr, 20 | } 21 | #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] 22 | #[non_exhaustive] 23 | pub enum Ipv6MulticastScope { 24 | InterfaceLocal, 25 | LinkLocal, 26 | RealmLocal, 27 | AdminLocal, 28 | SiteLocal, 29 | OrganizationLocal, 30 | Global, 31 | } 32 | impl IpAddr { 33 | pub const fn is_unspecified(&self) -> bool { 34 | match self { 35 | IpAddr::V4(ip) => ip.is_unspecified(), 36 | IpAddr::V6(ip) => ip.is_unspecified(), 37 | } 38 | } 39 | pub const fn is_loopback(&self) -> bool { 40 | match self { 41 | IpAddr::V4(ip) => ip.is_loopback(), 42 | IpAddr::V6(ip) => ip.is_loopback(), 43 | } 44 | } 45 | pub const fn is_global(&self) -> bool { 46 | match self { 47 | IpAddr::V4(ip) => ip.is_global(), 48 | IpAddr::V6(ip) => ip.is_global(), 49 | } 50 | } 51 | pub const fn is_multicast(&self) -> bool { 52 | match self { 53 | IpAddr::V4(ip) => ip.is_multicast(), 54 | IpAddr::V6(ip) => ip.is_multicast(), 55 | } 56 | } 57 | pub const fn is_documentation(&self) -> bool { 58 | match self { 59 | IpAddr::V4(ip) => ip.is_documentation(), 60 | IpAddr::V6(ip) => ip.is_documentation(), 61 | } 62 | } 63 | pub const fn is_benchmarking(&self) -> bool { 64 | match self { 65 | IpAddr::V4(ip) => ip.is_benchmarking(), 66 | IpAddr::V6(ip) => ip.is_benchmarking(), 67 | } 68 | } 69 | pub const fn is_ipv4(&self) -> bool { 70 | matches!(self, IpAddr::V4(_)) 71 | } 72 | pub const fn is_ipv6(&self) -> bool { 73 | matches!(self, IpAddr::V6(_)) 74 | } 75 | pub const fn to_canonical(&self) -> IpAddr { 76 | match self { 77 | &v4 @ IpAddr::V4(_) => v4, 78 | IpAddr::V6(v6) => v6.to_canonical(), 79 | } 80 | } 81 | } 82 | impl Ipv4Addr { 83 | pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { 84 | Ipv4Addr { 85 | inner: c::in_addr { 86 | s_addr: u32::from_ne_bytes([a, b, c, d]), 87 | }, 88 | } 89 | } 90 | pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); 91 | #[doc(alias = "INADDR_ANY")] 92 | pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); 93 | pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); 94 | pub const fn octets(&self) -> [u8; 4] { 95 | self.inner.s_addr.to_ne_bytes() 96 | } 97 | pub const fn is_unspecified(&self) -> bool { 98 | self.inner.s_addr == 0 99 | } 100 | pub const fn is_loopback(&self) -> bool { 101 | self.octets()[0] == 127 102 | } 103 | pub const fn is_private(&self) -> bool { 104 | match self.octets() { 105 | [10, ..] => true, 106 | [172, b, ..] if b >= 16 && b <= 31 => true, 107 | [192, 168, ..] => true, 108 | _ => false, 109 | } 110 | } 111 | pub const fn is_link_local(&self) -> bool { 112 | matches!(self.octets(), [169, 254, ..]) 113 | } 114 | pub const fn is_global(&self) -> bool { 115 | if u32::from_be_bytes(self.octets()) == 0xc0000009 116 | || u32::from_be_bytes(self.octets()) == 0xc000000a 117 | { 118 | return true; 119 | } 120 | !self.is_private() && !self.is_loopback() && !self.is_link_local() 121 | && !self.is_broadcast() && !self.is_documentation() && !self.is_shared() 122 | && !(self.octets()[0] == 192 && self.octets()[1] == 0 123 | && self.octets()[2] == 0) && !self.is_reserved() 124 | && !self.is_benchmarking() && self.octets()[0] != 0 125 | } 126 | pub const fn is_shared(&self) -> bool { 127 | self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) 128 | } 129 | pub const fn is_benchmarking(&self) -> bool { 130 | self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 131 | } 132 | pub const fn is_reserved(&self) -> bool { 133 | self.octets()[0] & 240 == 240 && !self.is_broadcast() 134 | } 135 | pub const fn is_multicast(&self) -> bool { 136 | self.octets()[0] >= 224 && self.octets()[0] <= 239 137 | } 138 | pub const fn is_broadcast(&self) -> bool { 139 | u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) 140 | } 141 | pub const fn is_documentation(&self) -> bool { 142 | matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _]) 143 | } 144 | pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { 145 | let [a, b, c, d] = self.octets(); 146 | Ipv6Addr { 147 | inner: c::in6_addr { 148 | s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d], 149 | }, 150 | } 151 | } 152 | pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { 153 | let [a, b, c, d] = self.octets(); 154 | Ipv6Addr { 155 | inner: c::in6_addr { 156 | s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d], 157 | }, 158 | } 159 | } 160 | } 161 | impl fmt::Display for IpAddr { 162 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 163 | match self { 164 | IpAddr::V4(ip) => ip.fmt(fmt), 165 | IpAddr::V6(ip) => ip.fmt(fmt), 166 | } 167 | } 168 | } 169 | impl fmt::Debug for IpAddr { 170 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 171 | fmt::Display::fmt(self, fmt) 172 | } 173 | } 174 | impl From for IpAddr { 175 | fn from(ipv4: Ipv4Addr) -> IpAddr { 176 | IpAddr::V4(ipv4) 177 | } 178 | } 179 | impl From for IpAddr { 180 | fn from(ipv6: Ipv6Addr) -> IpAddr { 181 | IpAddr::V6(ipv6) 182 | } 183 | } 184 | impl fmt::Display for Ipv4Addr { 185 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 186 | let octets = self.octets(); 187 | if fmt.precision().is_none() && fmt.width().is_none() { 188 | write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) 189 | } else { 190 | const IPV4_BUF_LEN: usize = 15; 191 | let mut buf = [0u8; IPV4_BUF_LEN]; 192 | let mut buf_slice = &mut buf[..]; 193 | write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) 194 | .unwrap(); 195 | let len = IPV4_BUF_LEN - buf_slice.len(); 196 | let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; 197 | fmt.pad(buf) 198 | } 199 | } 200 | } 201 | impl fmt::Debug for Ipv4Addr { 202 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 203 | fmt::Display::fmt(self, fmt) 204 | } 205 | } 206 | impl Clone for Ipv4Addr { 207 | fn clone(&self) -> Ipv4Addr { 208 | *self 209 | } 210 | } 211 | impl PartialEq for Ipv4Addr { 212 | fn eq(&self, other: &Ipv4Addr) -> bool { 213 | self.inner.s_addr == other.inner.s_addr 214 | } 215 | } 216 | impl PartialEq for IpAddr { 217 | fn eq(&self, other: &Ipv4Addr) -> bool { 218 | match self { 219 | IpAddr::V4(v4) => v4 == other, 220 | IpAddr::V6(_) => false, 221 | } 222 | } 223 | } 224 | impl PartialEq for Ipv4Addr { 225 | fn eq(&self, other: &IpAddr) -> bool { 226 | match other { 227 | IpAddr::V4(v4) => self == v4, 228 | IpAddr::V6(_) => false, 229 | } 230 | } 231 | } 232 | impl Eq for Ipv4Addr {} 233 | impl hash::Hash for Ipv4Addr { 234 | fn hash(&self, s: &mut H) { 235 | { self.inner.s_addr }.hash(s) 236 | } 237 | } 238 | impl PartialOrd for Ipv4Addr { 239 | fn partial_cmp(&self, other: &Ipv4Addr) -> Option { 240 | Some(self.cmp(other)) 241 | } 242 | } 243 | impl PartialOrd for IpAddr { 244 | fn partial_cmp(&self, other: &Ipv4Addr) -> Option { 245 | match self { 246 | IpAddr::V4(v4) => v4.partial_cmp(other), 247 | IpAddr::V6(_) => Some(Ordering::Greater), 248 | } 249 | } 250 | } 251 | impl PartialOrd for Ipv4Addr { 252 | fn partial_cmp(&self, other: &IpAddr) -> Option { 253 | match other { 254 | IpAddr::V4(v4) => self.partial_cmp(v4), 255 | IpAddr::V6(_) => Some(Ordering::Less), 256 | } 257 | } 258 | } 259 | impl Ord for Ipv4Addr { 260 | fn cmp(&self, other: &Ipv4Addr) -> Ordering { 261 | u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) 262 | } 263 | } 264 | impl IntoInner for Ipv4Addr { 265 | fn into_inner(self) -> c::in_addr { 266 | self.inner 267 | } 268 | } 269 | impl From for u32 { 270 | fn from(ip: Ipv4Addr) -> u32 { 271 | let ip = ip.octets(); 272 | u32::from_be_bytes(ip) 273 | } 274 | } 275 | impl From for Ipv4Addr { 276 | fn from(ip: u32) -> Ipv4Addr { 277 | Ipv4Addr::from(ip.to_be_bytes()) 278 | } 279 | } 280 | impl From<[u8; 4]> for Ipv4Addr { 281 | fn from(octets: [u8; 4]) -> Ipv4Addr { 282 | Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) 283 | } 284 | } 285 | impl From<[u8; 4]> for IpAddr { 286 | fn from(octets: [u8; 4]) -> IpAddr { 287 | IpAddr::V4(Ipv4Addr::from(octets)) 288 | } 289 | } 290 | impl Ipv6Addr { 291 | pub const fn new( 292 | a: u16, 293 | b: u16, 294 | c: u16, 295 | d: u16, 296 | e: u16, 297 | f: u16, 298 | g: u16, 299 | h: u16, 300 | ) -> Ipv6Addr { 301 | let addr16 = [ 302 | a.to_be(), 303 | b.to_be(), 304 | c.to_be(), 305 | d.to_be(), 306 | e.to_be(), 307 | f.to_be(), 308 | g.to_be(), 309 | h.to_be(), 310 | ]; 311 | Ipv6Addr { 312 | inner: c::in6_addr { 313 | s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, 314 | }, 315 | } 316 | } 317 | pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); 318 | pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); 319 | pub const fn segments(&self) -> [u16; 8] { 320 | let [a, b, c, d, e, f, g, h] = unsafe { 321 | transmute::<_, [u16; 8]>(self.inner.s6_addr) 322 | }; 323 | [ 324 | u16::from_be(a), 325 | u16::from_be(b), 326 | u16::from_be(c), 327 | u16::from_be(d), 328 | u16::from_be(e), 329 | u16::from_be(f), 330 | u16::from_be(g), 331 | u16::from_be(h), 332 | ] 333 | } 334 | pub const fn is_unspecified(&self) -> bool { 335 | u128::from_be_bytes(self.octets()) 336 | == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) 337 | } 338 | pub const fn is_loopback(&self) -> bool { 339 | u128::from_be_bytes(self.octets()) 340 | == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) 341 | } 342 | pub const fn is_global(&self) -> bool { 343 | match self.multicast_scope() { 344 | Some(Ipv6MulticastScope::Global) => true, 345 | None => self.is_unicast_global(), 346 | _ => false, 347 | } 348 | } 349 | pub const fn is_unique_local(&self) -> bool { 350 | (self.segments()[0] & 0xfe00) == 0xfc00 351 | } 352 | pub const fn is_unicast(&self) -> bool { 353 | !self.is_multicast() 354 | } 355 | pub const fn is_unicast_link_local(&self) -> bool { 356 | (self.segments()[0] & 0xffc0) == 0xfe80 357 | } 358 | pub const fn is_documentation(&self) -> bool { 359 | (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) 360 | } 361 | pub const fn is_benchmarking(&self) -> bool { 362 | (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) 363 | && (self.segments()[2] == 0) 364 | } 365 | pub const fn is_unicast_global(&self) -> bool { 366 | self.is_unicast() && !self.is_loopback() && !self.is_unicast_link_local() 367 | && !self.is_unique_local() && !self.is_unspecified() 368 | && !self.is_documentation() 369 | } 370 | pub const fn multicast_scope(&self) -> Option { 371 | if self.is_multicast() { 372 | match self.segments()[0] & 0x000f { 373 | 1 => Some(Ipv6MulticastScope::InterfaceLocal), 374 | 2 => Some(Ipv6MulticastScope::LinkLocal), 375 | 3 => Some(Ipv6MulticastScope::RealmLocal), 376 | 4 => Some(Ipv6MulticastScope::AdminLocal), 377 | 5 => Some(Ipv6MulticastScope::SiteLocal), 378 | 8 => Some(Ipv6MulticastScope::OrganizationLocal), 379 | 14 => Some(Ipv6MulticastScope::Global), 380 | _ => None, 381 | } 382 | } else { 383 | None 384 | } 385 | } 386 | pub const fn is_multicast(&self) -> bool { 387 | (self.segments()[0] & 0xff00) == 0xff00 388 | } 389 | pub const fn to_ipv4_mapped(&self) -> Option { 390 | match self.octets() { 391 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { 392 | Some(Ipv4Addr::new(a, b, c, d)) 393 | } 394 | _ => None, 395 | } 396 | } 397 | pub const fn to_ipv4(&self) -> Option { 398 | if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { 399 | let [a, b] = ab.to_be_bytes(); 400 | let [c, d] = cd.to_be_bytes(); 401 | Some(Ipv4Addr::new(a, b, c, d)) 402 | } else { 403 | None 404 | } 405 | } 406 | pub const fn to_canonical(&self) -> IpAddr { 407 | if let Some(mapped) = self.to_ipv4_mapped() { 408 | return IpAddr::V4(mapped); 409 | } 410 | IpAddr::V6(*self) 411 | } 412 | pub const fn octets(&self) -> [u8; 16] { 413 | self.inner.s6_addr 414 | } 415 | } 416 | impl fmt::Display for Ipv6Addr { 417 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 418 | if f.precision().is_none() && f.width().is_none() { 419 | let segments = self.segments(); 420 | if self.is_unspecified() { 421 | f.write_str("::") 422 | } else if self.is_loopback() { 423 | f.write_str("::1") 424 | } else if let Some(ipv4) = self.to_ipv4() { 425 | match segments[5] { 426 | 0 => write!(f, "::{}", ipv4), 427 | 0xffff => write!(f, "::ffff:{}", ipv4), 428 | _ => unreachable!(), 429 | } 430 | } else { 431 | #[derive(Copy, Clone, Default)] 432 | struct Span { 433 | start: usize, 434 | len: usize, 435 | } 436 | let zeroes = { 437 | let mut longest = Span::default(); 438 | let mut current = Span::default(); 439 | for (i, &segment) in segments.iter().enumerate() { 440 | if segment == 0 { 441 | if current.len == 0 { 442 | current.start = i; 443 | } 444 | current.len += 1; 445 | if current.len > longest.len { 446 | longest = current; 447 | } 448 | } else { 449 | current = Span::default(); 450 | } 451 | } 452 | longest 453 | }; 454 | /// Write a colon-separated part of the address 455 | #[inline] 456 | fn fmt_subslice( 457 | f: &mut fmt::Formatter<'_>, 458 | chunk: &[u16], 459 | ) -> fmt::Result { 460 | if let Some((first, tail)) = chunk.split_first() { 461 | write!(f, "{:x}", first)?; 462 | for segment in tail { 463 | f.write_char(':')?; 464 | write!(f, "{:x}", segment)?; 465 | } 466 | } 467 | Ok(()) 468 | } 469 | if zeroes.len > 1 { 470 | fmt_subslice(f, &segments[..zeroes.start])?; 471 | f.write_str("::")?; 472 | fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) 473 | } else { 474 | fmt_subslice(f, &segments) 475 | } 476 | } 477 | } else { 478 | const IPV6_BUF_LEN: usize = (4 * 8) + 7; 479 | let mut buf = [0u8; IPV6_BUF_LEN]; 480 | let mut buf_slice = &mut buf[..]; 481 | write!(buf_slice, "{}", self).unwrap(); 482 | let len = IPV6_BUF_LEN - buf_slice.len(); 483 | let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; 484 | f.pad(buf) 485 | } 486 | } 487 | } 488 | impl fmt::Debug for Ipv6Addr { 489 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 490 | fmt::Display::fmt(self, fmt) 491 | } 492 | } 493 | impl Clone for Ipv6Addr { 494 | fn clone(&self) -> Ipv6Addr { 495 | *self 496 | } 497 | } 498 | impl PartialEq for Ipv6Addr { 499 | fn eq(&self, other: &Ipv6Addr) -> bool { 500 | self.inner.s6_addr == other.inner.s6_addr 501 | } 502 | } 503 | impl PartialEq for Ipv6Addr { 504 | fn eq(&self, other: &IpAddr) -> bool { 505 | match other { 506 | IpAddr::V4(_) => false, 507 | IpAddr::V6(v6) => self == v6, 508 | } 509 | } 510 | } 511 | impl PartialEq for IpAddr { 512 | fn eq(&self, other: &Ipv6Addr) -> bool { 513 | match self { 514 | IpAddr::V4(_) => false, 515 | IpAddr::V6(v6) => v6 == other, 516 | } 517 | } 518 | } 519 | impl Eq for Ipv6Addr {} 520 | impl hash::Hash for Ipv6Addr { 521 | fn hash(&self, s: &mut H) { 522 | self.inner.s6_addr.hash(s) 523 | } 524 | } 525 | impl PartialOrd for Ipv6Addr { 526 | fn partial_cmp(&self, other: &Ipv6Addr) -> Option { 527 | Some(self.cmp(other)) 528 | } 529 | } 530 | impl PartialOrd for IpAddr { 531 | fn partial_cmp(&self, other: &Ipv6Addr) -> Option { 532 | match self { 533 | IpAddr::V4(_) => Some(Ordering::Less), 534 | IpAddr::V6(v6) => v6.partial_cmp(other), 535 | } 536 | } 537 | } 538 | impl PartialOrd for Ipv6Addr { 539 | fn partial_cmp(&self, other: &IpAddr) -> Option { 540 | match other { 541 | IpAddr::V4(_) => Some(Ordering::Greater), 542 | IpAddr::V6(v6) => self.partial_cmp(v6), 543 | } 544 | } 545 | } 546 | impl Ord for Ipv6Addr { 547 | fn cmp(&self, other: &Ipv6Addr) -> Ordering { 548 | self.segments().cmp(&other.segments()) 549 | } 550 | } 551 | impl AsInner for Ipv6Addr { 552 | fn as_inner(&self) -> &c::in6_addr { 553 | &self.inner 554 | } 555 | } 556 | impl FromInner for Ipv6Addr { 557 | fn from_inner(addr: c::in6_addr) -> Ipv6Addr { 558 | Ipv6Addr { inner: addr } 559 | } 560 | } 561 | impl From for u128 { 562 | fn from(ip: Ipv6Addr) -> u128 { 563 | let ip = ip.octets(); 564 | u128::from_be_bytes(ip) 565 | } 566 | } 567 | impl From for Ipv6Addr { 568 | fn from(ip: u128) -> Ipv6Addr { 569 | Ipv6Addr::from(ip.to_be_bytes()) 570 | } 571 | } 572 | impl From<[u8; 16]> for Ipv6Addr { 573 | fn from(octets: [u8; 16]) -> Ipv6Addr { 574 | let inner = c::in6_addr { s6_addr: octets }; 575 | Ipv6Addr::from_inner(inner) 576 | } 577 | } 578 | impl From<[u16; 8]> for Ipv6Addr { 579 | fn from(segments: [u16; 8]) -> Ipv6Addr { 580 | let [a, b, c, d, e, f, g, h] = segments; 581 | Ipv6Addr::new(a, b, c, d, e, f, g, h) 582 | } 583 | } 584 | impl From<[u8; 16]> for IpAddr { 585 | fn from(octets: [u8; 16]) -> IpAddr { 586 | IpAddr::V6(Ipv6Addr::from(octets)) 587 | } 588 | } 589 | impl From<[u16; 8]> for IpAddr { 590 | fn from(segments: [u16; 8]) -> IpAddr { 591 | IpAddr::V6(Ipv6Addr::from(segments)) 592 | } 593 | } 594 | --------------------------------------------------------------------------------