diff --git a/Cargo.toml b/Cargo.toml index d884468..34fbac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ description = """ Mercurial repository changelog parser. It allows to get any revision with creation date, user, parents, branch and files. """ -edition = "2018" +edition = "2021" homepage = "https://github.com/kilork/hg-parser" keywords = ["hg", "mercurial", "parser"] license = "GPL-2.0-or-later" @@ -14,14 +14,14 @@ version = "0.8.0" rust-version = "1.78" [dependencies] -bitflags = {version = "2.5", default-features = false} -byteorder = {version = "1.3", default-features = false} -chrono = {version = "0.4", features = ["clock"], default-features = false} +bitflags = { version = "2.5", default-features = false } +byteorder = { version = "1.3", default-features = false } +chrono = { version = "0.4", features = ["clock"], default-features = false } flate2 = "1.0" -lazy_static = {version = "1.4", default-features = false} -lru-cache = {version = "0.1", default-features = false} -nom = {version = "7.1", default-features = false, features = ["alloc"]} -ordered-parallel-iterator = {version = "0.2", default-features = false} -rayon = {version = "1.4", default-features = false} -sha-1 = {version = "0.10", default-features = false} -thiserror = "1" +lazy_static = { version = "1.4", default-features = false } +lru-cache = { version = "0.1", default-features = false } +nom = { version = "7.1", default-features = false, features = ["alloc"] } +ordered-parallel-iterator = { version = "0.2", default-features = false } +rayon = { version = "1.4", default-features = false } +sha-1 = { version = "0.10", default-features = false } +thiserror = "1" \ No newline at end of file diff --git a/README.md b/README.md index 1f1cb6f..eb51d4f 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,16 @@ hg-parser = "0.8" ### Use case - Analyse revision log and export to ```git fast-import``` format -```rust#ignore -use hg_parser::{file_content, FileType, ManifestEntryDetails, MercurialRepository, Revision}; +```rust, no_run +use std::{ + env::args, + io::Write, + path::{Path, PathBuf}, + string::ParseError, + time::Instant, +}; -use std::env::args; -use std::io::Write; -use std::path::{Path, PathBuf}; -use std::string::ParseError; -use std::time::Instant; +use hg_parser::{file_content, FileType, ManifestEntryDetails, MercurialRepository, Revision}; fn main() -> Result<(), Error> { let path: PathBuf = args().nth(1).expect("path not provided").parse()?; diff --git a/examples/git-fast-import.rs b/examples/git-fast-import.rs index 3b3d2e9..f2ca5ac 100644 --- a/examples/git-fast-import.rs +++ b/examples/git-fast-import.rs @@ -1,10 +1,12 @@ -use hg_parser::{file_content, FileType, ManifestEntryDetails, MercurialRepository, Revision}; +use std::{ + env::args, + io::Write, + path::{Path, PathBuf}, + string::ParseError, + time::Instant, +}; -use std::env::args; -use std::io::Write; -use std::path::{Path, PathBuf}; -use std::string::ParseError; -use std::time::Instant; +use hg_parser::{file_content, FileType, ManifestEntryDetails, MercurialRepository, Revision}; fn main() -> Result<(), Error> { let path: PathBuf = args().nth(1).expect("path not provided").parse()?; diff --git a/src/error.rs b/src/error.rs index 67dde15..b0617db 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +use crate::types::RepositoryRequire; + #[derive(Debug, thiserror::Error)] pub enum ErrorKind { /// Parser failed. @@ -20,7 +22,7 @@ pub enum ErrorKind { InvalidDateTime(String), /// Requirement in ``.hg/requires`` is not supported. #[error("unknown requirement {0}")] - UnknownRequirement(String), + UnknownRequirement(RepositoryRequire), /// Manifest issue. #[error("manifest issue {0}")] Manifest(String), diff --git a/src/lib.rs b/src/lib.rs index b096654..ac3d003 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,16 +22,23 @@ mod revisionlog; mod types; use cache::{Cachable, Cache}; -pub use changeset::*; -pub use error::ErrorKind; use manifest::Manifest; use path::{fncache_fsencode, simple_fsencode, MPath, MPathElement}; use revisionlog::RevisionLog; use types::{MercurialTag, NodeHash, RepositoryRequire}; +pub use changeset::*; +pub use error::ErrorKind; pub use manifest::{FileType, ManifestEntry, ManifestEntryDetails}; pub use types::{Revision, RevisionRange}; +/// Options for mercurial repository. +#[derive(Default)] +pub struct MercurialRepositoryOptions { + /// Threat unknown requirements as warnings. + pub ignore_unknown_requirements: bool, +} + #[derive(Debug)] /// Mercurial repository. Top-level structure for access to change sets and tags. pub struct MercurialRepository { @@ -44,9 +51,17 @@ pub struct MercurialRepository { impl MercurialRepository { /// Opens `MercurialRepository` at `root_path`. pub fn open>(root_path: P) -> Result { + Self::open_with_options(root_path, Default::default()) + } + + /// Opens `MercurialRepository` at `root_path` with options. + pub fn open_with_options>( + root_path: P, + options: MercurialRepositoryOptions, + ) -> Result { let base = root_path.as_ref().join(".hg"); - let requires = MercurialRepository::load_requires(&base)?; + let requires = MercurialRepository::load_requires(&base, &options)?; let store = base.join("store"); @@ -142,13 +157,23 @@ impl MercurialRepository { fn load_requires>( path: P, - ) -> Result, std::io::Error> { + options: &MercurialRepositoryOptions, + ) -> Result, ErrorKind> { let requires_path = path.as_ref().join("requires"); let file = File::open(requires_path)?; - Ok(BufReader::new(file) - .lines() - .map(|x| x.unwrap().parse().expect("could not parse requirement")) - .collect()) + let lines = BufReader::new(file).lines().map_while(Result::ok); + if options.ignore_unknown_requirements { + lines + .map(|x| match x.parse() { + Err(ErrorKind::UnknownRequirement(r)) => Ok(r), + other => other, + }) + .collect() + } else { + Ok(lines + .map(|x| x.parse().expect("could not parse requirement")) + .collect()) + } } fn fsencode_path(&self, elements: &[MPathElement]) -> PathBuf { diff --git a/src/types.rs b/src/types.rs index 713b2e9..efdc249 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,23 +1,24 @@ use std::{ - fmt::{self, Debug, Display}, + fmt::{self, Display}, ops::{Add, Range, Sub}, rc::Rc, str::FromStr, sync::Arc, }; -/// Repository requires flags. -/// Repositories contain a file (``.hg/requires``) containing a list of -/// features/capabilities that are *required* for clients to interface -/// with the repository. use super::error::ErrorKind; use bitflags::bitflags; use chrono::{ DateTime as ChronoDateTime, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone, }; use sha1::{Digest, Sha1}; -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub(crate) enum RepositoryRequire { + +/// Repository requires flags. +/// Repositories contain a file (``.hg/requires``) containing a list of +/// features/capabilities that are *required* for clients to interface +/// with the repository. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum RepositoryRequire { /// When present, revlogs are version 1 (**RevlogNG**). Revlogv1, /// The **store** repository layout is used. @@ -45,6 +46,10 @@ pub(crate) enum RepositoryRequire { TreeManifest, /// The working directory is sparse (only contains a subset of files). ExpSparse, + /// Safe behaviour for all shares that access a repository. + ShareSafe, + /// Unknown requirement. + Unknown(String), } impl FromStr for RepositoryRequire { @@ -64,7 +69,29 @@ impl FromStr for RepositoryRequire { "manifestv2" => Ok(Manifestv2), "treemanifest" => Ok(TreeManifest), "exp-sparse" => Ok(ExpSparse), - other => Err(ErrorKind::UnknownRequirement(other.into())), + "share-safe" => Ok(ShareSafe), + other => Err(ErrorKind::UnknownRequirement(Self::Unknown(other.into()))), + } + } +} + +impl std::fmt::Display for RepositoryRequire { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use RepositoryRequire::*; + match self { + Revlogv1 => "revlogv1".fmt(f), + Store => "store".fmt(f), + FnCache => "fncache".fmt(f), + Shared => "shared".fmt(f), + RelShared => "relshared".fmt(f), + DotEncode => "dotencode".fmt(f), + ParentDelta => "parentdelta".fmt(f), + GeneralDelta => "generaldelta".fmt(f), + Manifestv2 => "manifestv2".fmt(f), + TreeManifest => "treemanifest".fmt(f), + ExpSparse => "exp-sparse".fmt(f), + ShareSafe => "share-safe".fmt(f), + Unknown(s) => s.fmt(f), } } } @@ -81,7 +108,7 @@ impl Revision { /// Return an open ended iterator from index. pub fn range(self) -> RevisionRange { - RevisionRange(self.0, std::u32::MAX) + RevisionRange(self.0, u32::MAX) } } @@ -221,13 +248,13 @@ impl AsRef<[u8]> for NodeHash { } } -impl Display for NodeHash { +impl std::fmt::Display for NodeHash { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self.to_hex(), fmt) } } -impl Debug for NodeHash { +impl std::fmt::Debug for NodeHash { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "NodeHash({})", self) } @@ -303,7 +330,7 @@ impl Fragment { } } -impl Debug for Fragment { +impl std::fmt::Debug for Fragment { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, diff --git a/templates/README.md b/templates/README.md index cece590..5615ce0 100644 --- a/templates/README.md +++ b/templates/README.md @@ -13,7 +13,7 @@ hg-parser = "0.8" ### Use case - Analyse revision log and export to ```git fast-import``` format -{{ codeblock "rust#ignore" ( read_to_str "examples/git-fast-import.rs" ) }} +{{ codeblock "rust, no_run" ( read_to_str "examples/git-fast-import.rs" ) }} ## Implementation details