mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 07:17:55 +03:00
sparse: start rust lib w/ basic parsing
Summary: Start of a rust library for dealing with sparse profiles. In this commit we just know how to parse a single profile given the bytes. Reviewed By: quark-zju Differential Revision: D35157974 fbshipit-source-id: af4dae36fef08b69e827c6cb4e571d89b7c7ffbd
This commit is contained in:
parent
f2667924e9
commit
e30270055f
@ -93,6 +93,7 @@ members = [
|
||||
"lib/revisionstore/types",
|
||||
"lib/revlogindex",
|
||||
"lib/runlog",
|
||||
"lib/sparse",
|
||||
"lib/spawn-ext",
|
||||
"lib/status",
|
||||
"lib/storemodel",
|
||||
|
9
eden/scm/lib/sparse/Cargo.toml
Normal file
9
eden/scm/lib/sparse/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
# @generated by autocargo
|
||||
|
||||
[package]
|
||||
name = "sparse"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1.32"
|
189
eden/scm/lib/sparse/src/lib.rs
Normal file
189
eden/scm/lib/sparse/src/lib.rs
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This software may be used and distributed according to the terms of the
|
||||
* GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Profile {
|
||||
// Where this profile came from (typically a file path).
|
||||
source: String,
|
||||
|
||||
// [include], [exclude] and %include
|
||||
entries: Vec<ProfileEntry>,
|
||||
|
||||
// [metadata]
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
hidden: Option<String>,
|
||||
version: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Pattern {
|
||||
Include(String),
|
||||
Exclude(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ProfileEntry {
|
||||
Pattern(Pattern),
|
||||
Profile(String),
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum SectionType {
|
||||
Include,
|
||||
Exclude,
|
||||
Metadata,
|
||||
}
|
||||
|
||||
impl SectionType {
|
||||
fn from_str(value: &str) -> Option<Self> {
|
||||
match value {
|
||||
"[include]" => Some(SectionType::Include),
|
||||
"[exclude]" => Some(SectionType::Exclude),
|
||||
"[metadata]" => Some(SectionType::Metadata),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Profile {
|
||||
pub fn from_bytes(data: impl AsRef<[u8]>, source: String) -> Result<Self, io::Error> {
|
||||
let mut prof: Profile = Default::default();
|
||||
let mut current_metadata_val: Option<&mut String> = None;
|
||||
let mut section_type = SectionType::Include;
|
||||
|
||||
for (mut line_num, line) in BufReader::new(data.as_ref()).lines().enumerate() {
|
||||
line_num += 1;
|
||||
|
||||
let line = line?;
|
||||
let trimmed = line.trim();
|
||||
|
||||
// Ingore comments and empty lines.
|
||||
if matches!(trimmed.chars().next(), Some('#' | ';') | None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(p) = trimmed.strip_prefix("%include ") {
|
||||
prof.entries
|
||||
.push(ProfileEntry::Profile(p.trim().to_string()));
|
||||
} else if let Some(section_start) = SectionType::from_str(trimmed) {
|
||||
section_type = section_start;
|
||||
current_metadata_val = None;
|
||||
} else if section_type == SectionType::Metadata {
|
||||
if line.starts_with(&[' ', '\t']) {
|
||||
// Continuation of multiline value.
|
||||
if let Some(ref mut val) = current_metadata_val {
|
||||
val.push('\n');
|
||||
val.push_str(trimmed);
|
||||
} else {
|
||||
tracing::warn!(%line, %source, line_num, "orphan metadata line");
|
||||
}
|
||||
} else {
|
||||
current_metadata_val = None;
|
||||
if let Some((key, val)) = trimmed.split_once(&['=', ':']) {
|
||||
let prof_val = match key.trim() {
|
||||
"description" => &mut prof.description,
|
||||
"title" => &mut prof.title,
|
||||
"hidden" => &mut prof.hidden,
|
||||
"version" => &mut prof.version,
|
||||
_ => {
|
||||
tracing::warn!(%line, %source, line_num, "ignoring uninteresting metadata key");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
current_metadata_val = Some(prof_val.insert(val.trim().to_string()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if trimmed.starts_with('/') {
|
||||
tracing::warn!(%line, %source, line_num, "ignoring sparse rule starting with /");
|
||||
continue;
|
||||
}
|
||||
|
||||
if section_type == SectionType::Include {
|
||||
prof.entries
|
||||
.push(ProfileEntry::Pattern(Pattern::Include(trimmed.to_string())));
|
||||
} else {
|
||||
prof.entries
|
||||
.push(ProfileEntry::Pattern(Pattern::Exclude(trimmed.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prof.source = source;
|
||||
|
||||
Ok(prof)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Returns a profile's (includes, excludes, profiles).
|
||||
fn split_prof(prof: &Profile) -> (Vec<&str>, Vec<&str>, Vec<&str>) {
|
||||
let (mut inc, mut exc, mut profs) = (vec![], vec![], vec![]);
|
||||
for entry in &prof.entries {
|
||||
match entry {
|
||||
ProfileEntry::Pattern(Pattern::Include(p)) => inc.push(p.as_ref()),
|
||||
ProfileEntry::Pattern(Pattern::Exclude(p)) => exc.push(p.as_ref()),
|
||||
ProfileEntry::Profile(p) => profs.push(p.as_ref()),
|
||||
}
|
||||
}
|
||||
(inc, exc, profs)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsing() {
|
||||
let got = Profile::from_bytes(
|
||||
b"
|
||||
; hello
|
||||
# there
|
||||
|
||||
a
|
||||
[metadata]
|
||||
boring = banana
|
||||
title = foo
|
||||
[include]
|
||||
glob:b/**/z
|
||||
/skip/me
|
||||
%include other.sparse
|
||||
[exclude]
|
||||
c
|
||||
/skip/me
|
||||
|
||||
[metadata]
|
||||
skip me
|
||||
description:howdy
|
||||
doody
|
||||
version : 123
|
||||
hidden=your eyes
|
||||
only
|
||||
|
||||
",
|
||||
"test".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(got.source, "test");
|
||||
|
||||
let (inc, exc, profs) = split_prof(&got);
|
||||
assert_eq!(inc, vec!["a", "glob:b/**/z"]);
|
||||
assert_eq!(exc, vec!["c"]);
|
||||
assert_eq!(profs, vec!["other.sparse"]);
|
||||
|
||||
assert_eq!(got.title.unwrap(), "foo");
|
||||
assert_eq!(got.description.unwrap(), "howdy\ndoody");
|
||||
assert_eq!(got.hidden.unwrap(), "your eyes\nonly");
|
||||
assert_eq!(got.version.unwrap(), "123");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user