///|
pub(all) struct IriR {
scheme : StringView
authority : StringView?
path : StringView
query : StringView?
fragment : StringView?
} derive(Debug, Eq, Default)
///|
pub fn IriR::IriR(
scheme : StringView,
authority : StringView?,
path : StringView,
query : StringView?,
fragment : StringView?,
) -> IriR {
IriR::{ scheme, authority, path, query, fragment }
}
///|
pub fn IriR::parse(_self : IriR, s : String) -> Result[IriR, IriError] {
match parse_iri.run(@parserc.Seq::from_string(s)) {
Some((Ok(iri), _)) => Ok(iri)
Some((Err(e), _)) => Err(e)
None => Err(IriError::UnexpectedEnd(0))
}
}
///|
pub fn IriR::parse_unchecked(_self : IriR, s : String) -> IriR {
match parse_iri.run(@parserc.Seq::from_string(s)) {
Some((Ok(iri), _)) => iri
Some((Err(_), _)) =>
// 尝试返回尽可能多的信息
IriR::{
scheme: "",
authority: None,
path: s,
query: None,
fragment: None,
}
None =>
IriR::{
scheme: "",
authority: None,
path: s,
query: None,
fragment: None,
}
}
}
///|
/// 合并 IRI
pub fn IriR::resolve(
self : IriR,
ref_iri : String,
remove_dot_segments? : Bool = true,
) -> Result[IriR, IriError] {
let ref_parsed = match
IriR::{ scheme: "", authority: None, path: "", query: None, fragment: None }.parse(
ref_iri,
) {
Ok(r) => r
Err(e) => return Err(e)
}
// 如果参考 IRI 是绝对的,直接返回
if ref_parsed.scheme.length() > 0 {
return Ok(ref_parsed)
}
// 合并各个组件
let scheme = self.scheme
let authority = if ref_parsed.authority is Some(_) {
ref_parsed.authority
} else {
self.authority
}
// 路径合并:参考路径不以 / 开头时,合并到 base 路径的目录
let path = if ref_parsed.path.length() > 0 && ref_parsed.path[0] == '/' {
ref_parsed.path
} else if ref_parsed.path.length() > 0 {
let base_path = self.path
let last_slash = base_path.rev_find("/")
match last_slash {
Some(idx) => base_path[0:idx + 1].to_owned() + ref_parsed.path.to_owned()
None =>
if base_path.is_empty() {
// base 为空,相对路径保持原样
ref_parsed.path
} else {
"/" + ref_parsed.path.to_owned()
}
}
} else {
self.path
}
let path = if remove_dot_segments {
fn_remove_dot_segments(path)
} else {
path
}
let query = if ref_parsed.query.unwrap_or_default().length() > 0 {
ref_parsed.query
} else {
self.query
}
let fragment = if ref_parsed.fragment.unwrap_or_default().length() > 0 {
ref_parsed.fragment
} else {
self.fragment
}
Ok(IriR::{ scheme, authority, path, query, fragment })
}
///|
pub fn IriR::relativize(self : IriR, target : IriR) -> Result[IriR, IriError] {
// 如果 scheme 或 authority 不同,无法相对化
if self.scheme != target.scheme || self.authority != target.authority {
return Ok(target)
}
// 计算相对路径
let path = if self.path == target.path {
""
} else if target.path.has_prefix(self.path) {
let suffix = target.path[self.path.length():].to_owned()
if !suffix.has_prefix("/") && suffix != "" {
"./" + suffix
} else {
suffix
}
} else {
target.path.to_owned()
}
Ok(IriR::{
scheme: "",
authority: None,
path,
query: target.query,
fragment: target.fragment,
})
}
///|
pub fn IriR::resolve_unchecked(
self : IriR,
ref_iri : String,
remove_dot_segments? : Bool = true,
) -> IriR {
// 尝试解析参考 IRI(忽略错误)
let ref_parsed = IriR::{
scheme: "",
authority: None,
path: "",
query: None,
fragment: None,
}.parse_unchecked(ref_iri)
// 如果参考 IRI 是绝对的,直接返回
if ref_parsed.scheme.length() > 0 {
return ref_parsed
}
// 合并各个组件
let scheme = self.scheme
let authority = if ref_parsed.authority is Some(_) {
ref_parsed.authority
} else {
self.authority
}
// 合并路径
let path = if ref_parsed.path.has_prefix("/") {
ref_parsed.path // 绝对路径,直接用
} else if ref_parsed.path.length() > 0 {
// 相对路径,合并到 base 的目录
let base_path = self.path
let last_slash = base_path.rev_find("/")
match last_slash {
Some(idx) =>
if idx == 0 {
// base = "/base",没有中间目录
// 直接拼接:"/base" + "/" + "relative/path"
base_path.to_owned() + "/" + ref_parsed.path.to_owned()
} else {
// base = "/dir/base"
base_path[0:idx + 1].to_owned() + ref_parsed.path.to_owned()
}
None => ref_parsed.path.to_owned() // base 为空,直接用 reference
}
} else {
self.path // reference 为空,用 base
}
let path = if remove_dot_segments {
fn_remove_dot_segments(path)
} else {
path
}
let query = if ref_parsed.query is Some(_) {
ref_parsed.query
} else {
self.query
}
let fragment = ref_parsed.fragment
IriR::{ scheme, authority, path, query, fragment }
}
///|
pub fn IriR::into_string(self : IriR) -> String {
let s = StringBuilder::new()
// scheme
if self.scheme.length() > 0 {
s.write_view(self.scheme)
s.write_char(':')
}
// authority
match self.authority {
Some(auth) => {
s.write_string("//")
if auth.length() > 0 {
// 添加 authority
s.write_view(auth)
}
}
None => ()
}
// path
s.write_view(self.path)
// query
match self.query {
Some(q) => {
s.write_char('?')
s.write_view(q)
}
None => ()
}
// fragment
match self.fragment {
Some(f) => {
s.write_char('#')
s.write_view(f)
}
None => ()
}
s.to_string()
}
///|
pub fn fn_remove_dot_segments(path : StringView) -> StringView {
if path.is_empty() {
return path
}
let starts_with_slash = path[0] == '/'
// 按 / 分割成段
let parts = path.split("/")
let result : Array[StringView] = []
for part in parts {
match part {
"" | "." => () // 跳过空段和当前目录
".." =>
if starts_with_slash {
// 绝对路径:可以移除上一级
if result.length() > 0 && result[result.length() - 1] != ".." {
let _ = result.pop()
}
// 相对路径
} else if result.length() > 0 && result[result.length() - 1] != ".." {
let _ = result.pop() // 移除前一普通段
}
// 如果 result 为空或最后一个是 ..,就丢弃这个 ..
_ => result.push(part)
}
}
// 重建路径
if result.is_empty() {
if starts_with_slash {
return "/"
} else {
return ""
}
}
let final_path = result.join("/")
if starts_with_slash {
"/" + final_path
} else {
final_path
}
}
///|
/// 解析 scheme(遇到 : 停止)
fn parse_scheme(
input : @parserc.Seq[Char],
) -> (StringView, @parserc.Seq[Char])? {
let chars : Array[Char] = []
let mut input = input
for ;; {
match input.uncons() {
Some((c, rest)) =>
if is_ascii_alphanumeric(c) {
chars.push(c)
input = rest
} else {
break
}
None => break
}
}
if chars.is_empty() {
None
} else {
let scheme = chars.fold(init="", fn(a, b) { a + b.to_string() })
match input.uncons() {
Some((':', rest)) => Some((scheme, rest))
_ => None
}
}
}
///|
/// 解析 authority(遇到 // 开始,遇到 / ? # 空格 停止)
fn parse_authority(
input : @parserc.Seq[Char],
) -> (StringView?, @parserc.Seq[Char])? {
match input.uncons() {
Some(('/', rest1)) =>
match rest1.uncons() {
Some(('/', rest2)) => {
let chars : Array[Char] = []
let mut input = rest2
for ;; {
match input.uncons() {
Some((c, r)) =>
if c != '/' && c != '?' && c != '#' && c != ' ' {
chars.push(c)
input = r
} else {
break
}
None => break
}
}
if chars.is_empty() {
Some((None, rest2))
} else {
let auth = chars.fold(init="", fn(a, b) { a + b.to_string() })
Some((Some(auth), input))
}
}
_ => Some((None, input))
}
_ => Some((None, input))
}
}
///|
/// 解析 path(遇到 ? # 空格 换行 停止)
fn parse_path(input : @parserc.Seq[Char]) -> (StringView, @parserc.Seq[Char])? {
let chars : Array[Char] = []
let mut input = input
for ;; {
match input.uncons() {
Some((c, rest)) =>
if c != '?' && c != '#' && c != ' ' && c != '\n' {
chars.push(c)
input = rest
} else {
break // ← 这里 break,但 input 是原始输入,没有消费任何字符
}
None => break
}
}
if chars.is_empty() {
// 没有消费任何字符,返回空 path 和原始输入
Some(("", input))
} else {
let path = chars.fold(init="", fn(a, b) { a + b.to_string() })
Some((path, input))
}
}
///|
/// 解析 query(遇到 ? 开始,遇到 # 或空白停止)
fn parse_query(
input : @parserc.Seq[Char],
) -> (StringView?, @parserc.Seq[Char])? {
let (first, rest) = match input.uncons() {
Some((c, rest)) => (c, rest)
None => return Some((None, input))
}
if first != '?' {
return Some((None, input))
}
let chars : Array[Char] = []
let mut rest_view = rest
for ;; {
match rest_view.uncons() {
Some((c, r)) =>
if c != '#' && c != ' ' && c != '\n' {
chars.push(c)
rest_view = r
} else {
break
}
None => break
}
}
if chars.is_empty() {
Some((None, input)) // 关键:回退到原始 input
} else {
let query = chars.fold(init="", fn(a, b) { a + b.to_string() })
Some((Some(query), rest_view))
}
}
///|
/// 解析 fragment(遇到 # 开始,遇到空白停止)
fn parse_fragment(
input : @parserc.Seq[Char],
) -> (StringView?, @parserc.Seq[Char])? {
match input.uncons() {
Some(('#', rest)) => {
let chars : Array[Char] = []
let mut rest_view = rest
for ;; {
match rest_view.uncons() {
Some((c, r)) =>
if c != ' ' && c != '\n' {
chars.push(c)
rest_view = r
} else {
break
}
None => break
}
}
if chars.is_empty() {
// fragment 为空,不消费 #
Some((None, input)) // ← 保持 input 不变
} else {
let fragment = chars.fold(init="", fn(a, b) { a + b.to_string() })
Some((Some(fragment), rest_view))
}
}
_ => Some((None, input))
}
}
///|
/// 完整的 IRI 解析器
pub let parse_iri : IriParser[IriR] = @parserc.Parser::new(fn(input) {
// 1. 解析 scheme
let (scheme, rest1) = parse_scheme(input).unwrap_or((""[:], input))
// 2. 尝试 query 和 fragment(快速路径)
let (query, rest2) = parse_query(rest1).unwrap_or((None, rest1))
let (fragment, rest3) = parse_fragment(rest2).unwrap_or((None, rest2))
// 3. 如果有 query 或 fragment,直接返回
if query is Some(_) || fragment is Some(_) {
return Some(
(
Ok(IriR::{ scheme, authority: None, path: ""[:], query, fragment }),
rest3,
),
)
}
// 4. 否则解析 authority、path、query、fragment
let (auth, rest4) = parse_authority(rest3).unwrap_or((None, rest3))
let (path, rest5) = parse_path(rest4).unwrap_or((""[:], rest4))
let (query2, rest6) = parse_query(rest5).unwrap_or((None, rest5))
let (fragment2, rest7) = parse_fragment(rest6).unwrap_or((None, rest6))
// 5. query/fragment 都为空时,剩余输入追加到 path
let final_path = if query2 is None(_) &&
fragment2 is None(_) &&
rest7.length() > 0 {
path + rest7.to_string()
} else {
path
}
// 6. 返回结果
Some(
(
Ok(IriR::{
scheme,
authority: auth,
path: final_path,
query: query2,
fragment: fragment2,
}),
rest7,
),
)
})