Skip to content

Commit

Permalink
new location framework
Browse files Browse the repository at this point in the history
  • Loading branch information
brunoczim committed Dec 31, 2023
1 parent 25854f9 commit 33b06b2
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 55 deletions.
164 changes: 154 additions & 10 deletions src/location/external.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,132 @@
use url::Url;
use std::{error::Error, fmt, mem};

pub type InvalidExternal = url::ParseError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InvalidExternal {
MissingScheme,
EmptyScheme,
InvalidSchemeStart(char),
InvalidSchemeChar(char),
}

impl fmt::Display for InvalidExternal {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::MissingScheme => {
write!(fmtr, "missing external location scheme")
},
Self::EmptyScheme => {
write!(fmtr, "external location scheme cannot be empty")
},
Self::InvalidSchemeStart(ch) => write!(
fmtr,
"invalid starting character {:?} in external location scheme",
ch
),
Self::InvalidSchemeChar(ch) => write!(
fmtr,
"invalid character {:?} in external location scheme",
ch
),
}
}
}

impl Error for InvalidExternal {}

pub fn parse<'a>(
input: &'a str,
) -> Result<(&'a External, ViewRef<'a>), InvalidExternal> {
let (scheme, rest) =
input.split_once("://").ok_or(InvalidExternal::MissingScheme)?;

let mut iter = scheme.chars();

let ch = iter.next().ok_or(InvalidExternal::EmptyScheme)?;
if !ch.is_ascii_alphabetic() {
Err(InvalidExternal::InvalidSchemeStart(ch))?;
}

for ch in iter {
if !ch.is_ascii_alphanumeric() && ch != '_' && ch != '-' {
Err(InvalidExternal::InvalidSchemeChar(ch))?;
}
}

let external_loc = External::from_ref_unchecked(input);

let view = if scheme == "other" {
ViewRef::Other(rest)
} else {
ViewRef::WithHost { scheme, rest }
};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
Ok((external_loc, view))
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct External {
pub url: Url,
contents: str,
}

impl External {
pub fn parse(input: &str) -> Result<Self, InvalidExternal> {
Ok(Self { url: Url::parse(input)? })
pub fn parse(input: &str) -> Result<&Self, InvalidExternal> {
let (external_loc, _) = parse(input)?;
Ok(external_loc)
}

pub fn parse_boxed(input: Box<str>) -> Result<Box<Self>, InvalidExternal> {
Self::parse(input.as_ref())?;
Ok(Self::from_box_unchecked(input))
}

pub fn raw_contents(&self) -> &str {
&self.contents
}

pub fn into_boxed(&self) -> Box<Self> {
Self::from_box_unchecked(Box::from(self.raw_contents()))
}

pub fn view(&self) -> ViewRef {
let Some((scheme, rest)) = self.raw_contents().split_once("://") else {
unreachable!()
};

if scheme == "other" {
ViewRef::Other(rest)
} else {
ViewRef::WithHost { scheme, rest }
}
}
pub(crate) const fn from_ref_unchecked(input: &str) -> &Self {
unsafe { mem::transmute(input) }
}
pub(crate) const fn from_box_unchecked(input: Box<str>) -> Box<Self> {
unsafe { mem::transmute(input) }
}
}
impl<'input> TryFrom<&'input str> for External {
impl Clone for Box<External> {
fn clone(&self) -> Self {
self.into_boxed()
}
}
impl<'input> TryFrom<&'input str> for &'input External {
type Error = InvalidExternal;
fn try_from(input: &'input str) -> Result<Self, Self::Error> {
Self::parse(input)
External::parse(input)
}
}
impl TryFrom<Box<str>> for External {
impl TryFrom<Box<str>> for Box<External> {
type Error = InvalidExternal;
fn try_from(input: Box<str>) -> Result<Self, Self::Error> {
Self::parse(&input[..])
External::parse_boxed(input)
}
}
Expand All @@ -34,3 +135,46 @@ impl AsRef<External> for External {
self
}
}
impl fmt::Display for External {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
write!(fmtr, "{}", self.raw_contents())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ViewRef<'a> {
WithHost { scheme: &'a str, rest: &'a str },
Other(&'a str),
}
impl<'a> TryFrom<&'a str> for ViewRef<'a> {
type Error = InvalidExternal;
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
Self::parse(input)
}
}
impl<'a> ViewRef<'a> {
pub fn parse(input: &'a str) -> Result<Self, InvalidExternal> {
let (_, view) = parse(input)?;
Ok(view)
}
pub fn to_boxed(&self) -> Box<External> {
let external_str = self.to_string();
External::from_box_unchecked(external_str.into_boxed_str())
}
}
impl<'a> fmt::Display for ViewRef<'a> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::WithHost { scheme, rest } => {
write!(fmtr, "{}://{}", scheme, rest)
},
Self::Other(rest) => write!(fmtr, "other://{}", rest),
}
}
}
145 changes: 108 additions & 37 deletions src/location/general.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use std::{error::Error, fmt};
use std::{error::Error, fmt, mem};

use super::{
external::{External, InvalidExternal},
id::{Id, InvalidId},
internal::{Internal, InvalidInternal, View},
path::{InvalidPath, Path},
internal::{Internal, InvalidInternal},
};

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -36,45 +34,118 @@ impl fmt::Display for InvalidLocation {

impl Error for InvalidLocation {}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Location {
Internal(Box<Internal>),
External(External),
}

/*
impl<P, I, E> Location<P, I, E>
where
P: AsRef<Path>,
I: AsRef<Id>,
E: AsExternal,
{
pub fn parse<'input>(input: &'input str) -> Result<Self, InvalidLocation>
where
I: TryFrom<&'input str, Error = InvalidId>,
P: TryFrom<&'input str, Error = InvalidPath>,
E: TryFrom<&'input str, Error = InvalidExternal>,
{
if input.contains("://") {
Ok(Self::External(E::try_from(input)?))
} else {
Ok(Self::Internal(View::try_from(input)?))
}
pub fn parse<'a>(
input: &'a str,
) -> Result<(&'a Location, ViewRef<'a>), InvalidLocation> {
let view = if input.contains("://") {
let external = External::parse(input)?;
ViewRef::External(external)
} else {
let internal = Internal::parse(input)?;
ViewRef::Internal(internal)
};
let location = Location::from_ref_unchecked(input);
Ok((location, view))
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Location {
contents: str,
}

impl Location {
pub fn parse(input: &str) -> Result<&Self, InvalidLocation> {
let (external_loc, _) = parse(input)?;
Ok(external_loc)
}

pub fn parse_boxed(input: Box<str>) -> Result<Box<Self>, InvalidLocation> {
Self::parse(input.as_ref())?;
Ok(Self::from_box_unchecked(input))
}

pub fn raw_contents(&self) -> &str {
&self.contents
}

pub fn into_boxed(&self) -> Box<Self> {
Self::from_box_unchecked(Box::from(self.raw_contents()))
}

pub(crate) const fn from_ref_unchecked(input: &str) -> &Self {
unsafe { mem::transmute(input) }
}

pub(crate) const fn from_box_unchecked(input: Box<str>) -> Box<Self> {
unsafe { mem::transmute(input) }
}
}

impl Clone for Box<Location> {
fn clone(&self) -> Self {
self.into_boxed()
}
}
*/

/*
impl<'input, P, I, E> TryFrom<&'input str> for Location<P, I, E>
where
P: AsRef<Path> + TryFrom<&'input str, Error = InvalidPath>,
I: AsRef<Id> + TryFrom<&'input str, Error = InvalidId>,
E: AsExternal + TryFrom<&'input str, Error = InvalidExternal>,
{
impl<'input> TryFrom<&'input str> for &'input Location {
type Error = InvalidLocation;

fn try_from(input: &'input str) -> Result<Self, Self::Error> {
Location::parse(input)
}
}

impl TryFrom<Box<str>> for Box<Location> {
type Error = InvalidLocation;

fn try_from(input: Box<str>) -> Result<Self, Self::Error> {
Location::parse_boxed(input)
}
}

impl AsRef<Location> for Location {
fn as_ref(&self) -> &Location {
self
}
}

impl fmt::Display for Location {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
write!(fmtr, "{}", self.raw_contents())
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ViewRef<'a> {
Internal(&'a Internal),
External(&'a External),
}

impl<'a> TryFrom<&'a str> for ViewRef<'a> {
type Error = InvalidLocation;

fn try_from(input: &'a str) -> Result<Self, Self::Error> {
Self::parse(input)
}
}
*/

impl<'a> ViewRef<'a> {
pub fn parse(input: &'a str) -> Result<Self, InvalidLocation> {
let (_, view) = parse(input)?;
Ok(view)
}

pub fn to_boxed(&self) -> Box<Location> {
let location_str = self.to_string();
Location::from_box_unchecked(location_str.into_boxed_str())
}
}

impl<'a> fmt::Display for ViewRef<'a> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Internal(internal) => write!(fmtr, "{}", internal),
Self::External(external) => write!(fmtr, "{}", external),
}
}
}
2 changes: 1 addition & 1 deletion src/location/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Id {
}
}

impl Clone for Box<Component> {
impl Clone for Box<Id> {
fn clone(&self) -> Self {
self.into_boxed()
}
Expand Down
Loading

0 comments on commit 33b06b2

Please sign in to comment.