mirror of
https://github.com/samsonjs/agate.git
synced 2026-03-25 09:05:50 +00:00
use YAML parser for sidecar files
The syntax so far is (compatible with) YAML.
This commit is contained in:
parent
8f2cfe7a8f
commit
544f577b59
4 changed files with 94 additions and 54 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -14,6 +14,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-rustls",
|
||||
"url",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -141,6 +142,12 @@ version = "0.2.85"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
|
|
@ -539,3 +546,12 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ once_cell = "1.5"
|
|||
percent-encoding = "2.1"
|
||||
rustls = "0.19.0"
|
||||
url = "2.2"
|
||||
yaml-rust = "0.4"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
|||
|
|
@ -30,11 +30,12 @@ fn main() -> Result {
|
|||
.init();
|
||||
}
|
||||
Runtime::new()?.block_on(async {
|
||||
let mimetypes = Arc::new(Mutex::new(FileOptions::new(PresetMeta::Parameters(
|
||||
let default = PresetMeta::Parameters(
|
||||
ARGS.language
|
||||
.as_ref()
|
||||
.map_or(String::new(), |lang| format!(";lang={}", lang)),
|
||||
))));
|
||||
);
|
||||
let mimetypes = Arc::new(Mutex::new(FileOptions::new(default)));
|
||||
let listener = TcpListener::bind(&ARGS.addrs[..]).await?;
|
||||
log::info!("Listening on {:?}...", ARGS.addrs);
|
||||
loop {
|
||||
|
|
|
|||
126
src/metadata.rs
126
src/metadata.rs
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
static SIDECAR_FILENAME: &str = ".meta";
|
||||
|
||||
|
|
@ -108,60 +108,82 @@ impl FileOptions {
|
|||
db.push(SIDECAR_FILENAME);
|
||||
let db = db.as_path();
|
||||
|
||||
if let Ok(file) = std::fs::File::open(db) {
|
||||
let r = BufReader::new(file);
|
||||
r.lines()
|
||||
// discard any I/O errors
|
||||
.filter_map(|line| line.ok())
|
||||
// filter out comment lines
|
||||
.filter(|line| !line.trim_start().starts_with('#'))
|
||||
.for_each(|line| {
|
||||
// split line at colon
|
||||
let parts = line.splitn(2, ':').collect::<Vec<_>>();
|
||||
// only continue if line fits the format
|
||||
if parts.len() == 2 {
|
||||
// generate workspace-unique path
|
||||
let mut path = db_dir.clone();
|
||||
path.push(parts[0].trim());
|
||||
// parse the line
|
||||
let header = parts[1].trim();
|
||||
if let Ok(contents) = std::fs::read_to_string(db) {
|
||||
let docs = match YamlLoader::load_from_str(&contents) {
|
||||
Ok(docs) => docs,
|
||||
Err(e) => {
|
||||
log::error!("Invalid YAML document in {:?}: {}", db, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Some(files) = docs.get(0).and_then(|hash| hash.as_hash()) {
|
||||
for (rel_path, header) in files {
|
||||
// from YAML to Rust types
|
||||
let rel_path = if let Some(rel_path) = rel_path.as_str() {
|
||||
rel_path
|
||||
} else {
|
||||
log::error!(
|
||||
"Expected string filename, but got {:?} in {:?}",
|
||||
rel_path,
|
||||
db
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let header = if let Some(header) = header.as_str() {
|
||||
header
|
||||
} else {
|
||||
log::error!("Expected string contents, but got {:?} in {:?}", header, db);
|
||||
continue;
|
||||
};
|
||||
|
||||
let preset = if header.is_empty() || header.starts_with(';') {
|
||||
PresetMeta::Parameters(header.to_string())
|
||||
} else if matches!(header.chars().next(), Some('1'..='6')) {
|
||||
if header.len() < 3
|
||||
|| !header.chars().nth(1).unwrap().is_ascii_digit()
|
||||
|| !header.chars().nth(2).unwrap().is_whitespace()
|
||||
{
|
||||
log::error!("Line for {:?} starts like a full header line, but it is incorrect; ignoring it.", path);
|
||||
return;
|
||||
}
|
||||
let separator = header.chars().nth(2).unwrap();
|
||||
if separator != ' ' {
|
||||
// the Gemini specification says that the third
|
||||
// character has to be a space, so correct any
|
||||
// other whitespace to it (e.g. tabs)
|
||||
log::warn!("Full Header line for {:?} has an invalid character, treating {:?} as a space.", path, separator);
|
||||
}
|
||||
let status = header.chars()
|
||||
.take(2)
|
||||
.collect::<String>()
|
||||
.parse::<u8>()
|
||||
// unwrap since we alread checked it's a number
|
||||
.unwrap();
|
||||
// not taking a slice here because the separator
|
||||
// might be a whitespace wider than a byte
|
||||
let meta = header.chars().skip(3).collect::<String>();
|
||||
PresetMeta::FullHeader(status, meta)
|
||||
} else {
|
||||
// must be a MIME type, but without status code
|
||||
PresetMeta::FullMime(header.to_string())
|
||||
};
|
||||
self.file_meta.insert(path, preset);
|
||||
}
|
||||
});
|
||||
// generate workspace-unique path
|
||||
let mut path = db_dir.clone();
|
||||
path.push(rel_path);
|
||||
|
||||
// parse the preset
|
||||
let preset = if header.is_empty() || header.starts_with(';') {
|
||||
PresetMeta::Parameters(header.to_string())
|
||||
} else if matches!(header.chars().next(), Some('1'..='6')) {
|
||||
if header.len() < 3
|
||||
|| !header.chars().nth(1).unwrap().is_ascii_digit()
|
||||
|| !header.chars().nth(2).unwrap().is_whitespace()
|
||||
{
|
||||
log::error!("Line for {:?} starts like a full header line, but it is incorrect; ignoring it.", path);
|
||||
return;
|
||||
}
|
||||
let separator = header.chars().nth(2).unwrap();
|
||||
if separator != ' ' {
|
||||
// the Gemini specification says that the third
|
||||
// character has to be a space, so correct any
|
||||
// other whitespace to it (e.g. tabs)
|
||||
log::warn!("Full Header line for {:?} has an invalid character, treating {:?} as a space.", path, separator);
|
||||
}
|
||||
let status = header
|
||||
.chars()
|
||||
.take(2)
|
||||
.collect::<String>()
|
||||
.parse::<u8>()
|
||||
// unwrap since we alread checked it's a number
|
||||
.unwrap();
|
||||
// not taking a slice here because the separator
|
||||
// might be a whitespace wider than a byte
|
||||
let meta = header.chars().skip(3).collect::<String>();
|
||||
PresetMeta::FullHeader(status, meta)
|
||||
} else {
|
||||
// must be a MIME type, but without status code
|
||||
PresetMeta::FullMime(header.to_string())
|
||||
};
|
||||
|
||||
self.file_meta.insert(path, preset);
|
||||
}
|
||||
} else {
|
||||
log::error!("no YAML document {:?}", db);
|
||||
return;
|
||||
};
|
||||
self.databases_read
|
||||
.insert(db_dir.clone(), SystemTime::now());
|
||||
} else {
|
||||
log::error!("could not read configuration file {:?}", db);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue